From 0c66441bbb5f2582703179b7024666c2d789c34c Mon Sep 17 00:00:00 2001 From: Matt Raible Date: Thu, 9 May 2024 09:55:06 -0600 Subject: [PATCH 01/17] migration application generated with JHipster current (source) --- .browserslistrc | 18 + .devcontainer/Dockerfile | 25 + .devcontainer/devcontainer.json | 48 + .editorconfig | 23 + .eslintignore | 8 + .eslintrc.json | 99 + .gitattributes | 150 + .gitignore | 159 + .husky/pre-commit | 5 + .jhipster/BloodPressure.json | 34 + .jhipster/Points.json | 42 + .jhipster/Preferences.json | 33 + .jhipster/Weight.json | 29 + .jhipster/modules/jhi-hooks.json | 10 + .lintstagedrc.js | 3 + .prettierignore | 8 + .prettierrc | 18 + .yo-rc.json | 55 + README.md | 255 + angular.json | 109 + build.gradle | 298 + checkstyle.xml | 19 + cypress.config.ts | 30 + gradle.properties | 65 + gradle/docker.gradle | 29 + gradle/profile_dev.gradle | 95 + gradle/profile_prod.gradle | 66 + gradle/sonar.gradle | 26 + gradle/war.gradle | 16 + gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 59821 bytes gradle/wrapper/gradle-wrapper.properties | 5 + gradle/zipkin.gradle | 3 + gradlew | 185 + gradlew.bat | 89 + jest.conf.js | 29 + ngsw-config.json | 21 + npmw | 42 + npmw.cmd | 31 + package-lock.json | 45855 ++++++++++++++++ package.json | 164 + settings.gradle | 22 + sonar-project.properties | 34 + src/main/docker/app.yml | 41 + .../docker/central-server-config/README.md | 1 + src/main/docker/elasticsearch.yml | 26 + .../grafana/provisioning/dashboards/JVM.json | 3778 ++ .../provisioning/dashboards/dashboard.yml | 11 + .../provisioning/datasources/datasource.yml | 50 + src/main/docker/jhipster-control-center.yml | 52 + src/main/docker/jib/entrypoint.sh | 39 + src/main/docker/monitoring.yml | 31 + src/main/docker/postgresql.yml | 15 + src/main/docker/prometheus/prometheus.yml | 31 + src/main/docker/sonar.yml | 13 + src/main/docker/zipkin.yml | 7 + .../jhipster/health/ApplicationWebXml.java | 19 + .../jhipster/health/GeneratedByJHipster.java | 13 + .../jhipster/health/TwentyOnePointsApp.java | 105 + .../health/aop/logging/LoggingAspect.java | 115 + .../health/config/ApplicationProperties.java | 32 + .../health/config/AsyncConfiguration.java | 48 + .../health/config/CRLFLogConverter.java | 56 + .../health/config/CacheConfiguration.java | 82 + .../org/jhipster/health/config/Constants.java | 15 + .../health/config/DatabaseConfiguration.java | 59 + .../config/DateTimeFormatConfiguration.java | 20 + .../config/ElasticsearchConfiguration.java | 105 + .../health/config/JacksonConfiguration.java | 51 + .../health/config/LiquibaseConfiguration.java | 69 + .../health/config/LocaleConfiguration.java | 26 + .../config/LoggingAspectConfiguration.java | 17 + .../health/config/LoggingConfiguration.java | 47 + .../health/config/SecurityConfiguration.java | 105 + .../StaticResourcesWebConfiguration.java | 59 + .../jhipster/health/config/WebConfigurer.java | 111 + .../jhipster/health/config/package-info.java | 4 + .../health/domain/AbstractAuditingEntity.java | 75 + .../org/jhipster/health/domain/Authority.java | 61 + .../jhipster/health/domain/BloodPressure.java | 139 + .../org/jhipster/health/domain/Points.java | 172 + .../jhipster/health/domain/Preferences.java | 125 + .../java/org/jhipster/health/domain/User.java | 234 + .../org/jhipster/health/domain/Weight.java | 121 + .../health/domain/enumeration/Units.java | 9 + .../jhipster/health/domain/package-info.java | 4 + .../management/SecurityMetersService.java | 51 + .../repository/AuthorityRepository.java | 9 + .../repository/BloodPressureRepository.java | 43 + .../health/repository/PointsRepository.java | 43 + .../repository/PreferencesRepository.java | 40 + .../health/repository/UserRepository.java | 36 + .../health/repository/WeightRepository.java | 43 + .../health/repository/package-info.java | 4 + .../search/BloodPressureSearchRepository.java | 69 + .../search/PointsSearchRepository.java | 68 + .../search/PreferencesSearchRepository.java | 56 + .../search/UserSearchRepository.java | 34 + .../search/WeightSearchRepository.java | 68 + .../repository/search/package-info.java | 4 + .../health/security/AuthoritiesConstants.java | 15 + .../security/DomainUserDetailsService.java | 64 + .../health/security/SecurityUtils.java | 100 + .../security/SpringSecurityAuditorAware.java | 18 + .../security/UserNotActivatedException.java | 19 + .../health/security/jwt/JWTConfigurer.java | 21 + .../health/security/jwt/JWTFilter.java | 47 + .../health/security/jwt/TokenProvider.java | 126 + .../health/security/package-info.java | 4 + .../service/ElasticsearchIndexService.java | 199 + .../service/EmailAlreadyUsedException.java | 10 + .../service/InvalidPasswordException.java | 10 + .../jhipster/health/service/MailService.java | 112 + .../jhipster/health/service/UserService.java | 337 + .../service/UsernameAlreadyUsedException.java | 10 + .../health/service/dto/AdminUserDTO.java | 196 + .../health/service/dto/PasswordChangeDTO.java | 39 + .../jhipster/health/service/dto/UserDTO.java | 51 + .../health/service/dto/package-info.java | 4 + .../health/service/mapper/UserMapper.java | 147 + .../health/service/mapper/package-info.java | 4 + .../jhipster/health/service/package-info.java | 4 + .../health/web/rest/AccountResource.java | 194 + .../web/rest/BloodPressureResource.java | 243 + .../web/rest/ClientForwardController.java | 17 + .../web/rest/ElasticsearchIndexResource.java | 64 + .../health/web/rest/PointsResource.java | 246 + .../health/web/rest/PreferencesResource.java | 218 + .../health/web/rest/PublicUserResource.java | 83 + .../health/web/rest/UserJWTController.java | 68 + .../health/web/rest/UserResource.java | 211 + .../health/web/rest/WeightResource.java | 237 + .../rest/errors/BadRequestAlertException.java | 42 + .../errors/EmailAlreadyUsedException.java | 11 + .../web/rest/errors/ErrorConstants.java | 17 + .../web/rest/errors/ExceptionTranslator.java | 222 + .../health/web/rest/errors/FieldErrorVM.java | 32 + .../rest/errors/InvalidPasswordException.java | 14 + .../errors/LoginAlreadyUsedException.java | 11 + .../health/web/rest/errors/package-info.java | 6 + .../health/web/rest/package-info.java | 4 + .../health/web/rest/vm/KeyAndPasswordVM.java | 27 + .../jhipster/health/web/rest/vm/LoginVM.java | 53 + .../health/web/rest/vm/ManagedUserVM.java | 35 + .../health/web/rest/vm/package-info.java | 4 + src/main/resources/.h2.server.properties | 5 + src/main/resources/banner.txt | 10 + src/main/resources/config/application-dev.yml | 117 + .../resources/config/application-prod.yml | 128 + src/main/resources/config/application-tls.yml | 19 + src/main/resources/config/application.yml | 246 + src/main/resources/config/bootstrap-prod.yml | 6 + src/main/resources/config/bootstrap.yml | 14 + .../00000000000000_initial_schema.xml | 121 + .../20221108000520_added_entity_Points.xml | 64 + ...000520_added_entity_constraints_Points.xml | 17 + .../20221108001452_added_entity_Weight.xml | 53 + ...001452_added_entity_constraints_Weight.xml | 17 + ...21108001642_added_entity_BloodPressure.xml | 57 + ...added_entity_constraints_BloodPressure.xml | 17 + ...0221108002021_added_entity_Preferences.xml | 52 + ...1_added_entity_constraints_Preferences.xml | 17 + .../config/liquibase/data/authority.csv | 3 + .../resources/config/liquibase/data/user.csv | 3 + .../config/liquibase/data/user_authority.csv | 4 + .../liquibase/fake-data/blood_pressure.csv | 11 + .../config/liquibase/fake-data/points.csv | 11 + .../liquibase/fake-data/preferences.csv | 11 + .../config/liquibase/fake-data/weight.csv | 11 + .../resources/config/liquibase/master.xml | 31 + src/main/resources/i18n/messages.properties | 21 + .../resources/i18n/messages_en.properties | 21 + .../resources/i18n/messages_fr.properties | 21 + src/main/resources/logback-spring.xml | 79 + src/main/resources/templates/error.html | 92 + .../templates/mail/activationEmail.html | 20 + .../templates/mail/creationEmail.html | 20 + .../templates/mail/passwordResetEmail.html | 22 + src/main/webapp/404.html | 58 + src/main/webapp/WEB-INF/web.xml | 13 + src/main/webapp/app/account/account.module.ts | 26 + src/main/webapp/app/account/account.route.ts | 17 + .../account/activate/activate.component.html | 16 + .../activate/activate.component.spec.ts | 69 + .../account/activate/activate.component.ts | 23 + .../app/account/activate/activate.route.ts | 11 + .../account/activate/activate.service.spec.ts | 47 + .../app/account/activate/activate.service.ts | 16 + .../password-reset-finish.component.html | 137 + .../password-reset-finish.component.spec.ts | 98 + .../finish/password-reset-finish.component.ts | 64 + .../finish/password-reset-finish.route.ts | 11 + .../password-reset-finish.service.spec.ts | 44 + .../finish/password-reset-finish.service.ts | 14 + .../init/password-reset-init.component.html | 81 + .../password-reset-init.component.spec.ts | 63 + .../init/password-reset-init.component.ts | 30 + .../init/password-reset-init.route.ts | 11 + .../init/password-reset-init.service.spec.ts | 43 + .../init/password-reset-init.service.ts | 14 + .../password-strength-bar.component.html | 10 + .../password-strength-bar.component.scss | 23 + .../password-strength-bar.component.spec.ts | 46 + .../password-strength-bar.component.ts | 72 + .../account/password/password.component.html | 152 + .../password/password.component.spec.ts | 104 + .../account/password/password.component.ts | 51 + .../app/account/password/password.route.ts | 13 + .../account/password/password.service.spec.ts | 44 + .../app/account/password/password.service.ts | 14 + .../account/register/register.component.html | 234 + .../register/register.component.spec.ts | 135 + .../account/register/register.component.ts | 81 + .../app/account/register/register.model.ts | 3 + .../app/account/register/register.route.ts | 11 + .../account/register/register.service.spec.ts | 48 + .../app/account/register/register.service.ts | 15 + .../account/settings/settings.component.html | 166 + .../settings/settings.component.spec.ts | 91 + .../account/settings/settings.component.ts | 64 + .../app/account/settings/settings.route.ts | 13 + .../webapp/app/admin/admin-routing.module.ts | 47 + .../configuration.component.html | 57 + .../configuration.component.spec.ts | 67 + .../configuration/configuration.component.ts | 35 + .../configuration/configuration.model.ts | 40 + .../configuration/configuration.module.ts | 12 + .../configuration/configuration.route.ts | 11 + .../configuration.service.spec.ts | 71 + .../configuration/configuration.service.ts | 28 + .../webapp/app/admin/docs/docs.component.html | 10 + .../webapp/app/admin/docs/docs.component.scss | 6 + .../webapp/app/admin/docs/docs.component.ts | 8 + src/main/webapp/app/admin/docs/docs.module.ts | 12 + src/main/webapp/app/admin/docs/docs.route.ts | 11 + ...elasticsearch-reindex-modal.component.html | 18 + .../elasticsearch-reindex-modal.component.ts | 16 + ...search-reindex-selected-modal.component.ts | 19 + .../elasticsearch-reindex.component.html | 31 + .../elasticsearch-reindex.component.ts | 41 + .../elasticsearch-reindex.module.ts | 23 + .../elasticsearch-reindex.route.ts | 13 + .../elasticsearch-reindex.service.ts | 16 + .../app/admin/elasticsearch-reindex/index.ts | 5 + .../app/admin/health/health.component.html | 48 + .../app/admin/health/health.component.spec.ts | 66 + .../app/admin/health/health.component.ts | 44 + .../webapp/app/admin/health/health.model.ts | 15 + .../webapp/app/admin/health/health.module.ts | 13 + .../webapp/app/admin/health/health.route.ts | 11 + .../app/admin/health/health.service.spec.ts | 48 + .../webapp/app/admin/health/health.service.ts | 15 + .../health/modal/health-modal.component.html | 36 + .../modal/health-modal.component.spec.ts | 112 + .../health/modal/health-modal.component.ts | 34 + src/main/webapp/app/admin/logs/log.model.ts | 15 + .../webapp/app/admin/logs/logs.component.html | 74 + .../app/admin/logs/logs.component.spec.ts | 83 + .../webapp/app/admin/logs/logs.component.ts | 48 + src/main/webapp/app/admin/logs/logs.module.ts | 12 + src/main/webapp/app/admin/logs/logs.route.ts | 11 + .../app/admin/logs/logs.service.spec.ts | 31 + .../webapp/app/admin/logs/logs.service.ts | 19 + .../jvm-memory/jvm-memory.component.html | 28 + .../blocks/jvm-memory/jvm-memory.component.ts | 19 + .../jvm-threads/jvm-threads.component.html | 55 + .../jvm-threads/jvm-threads.component.ts | 55 + .../metrics-cache.component.html | 42 + .../metrics-cache/metrics-cache.component.ts | 23 + .../metrics-datasource.component.html | 57 + .../metrics-datasource.component.ts | 23 + .../metrics-endpoints-requests.component.html | 24 + .../metrics-endpoints-requests.component.ts | 19 + .../metrics-garbagecollector.component.html | 92 + .../metrics-garbagecollector.component.ts | 19 + .../metrics-modal-threads.component.html | 90 + .../metrics-modal-threads.component.ts | 59 + .../metrics-request.component.html | 26 + .../metrics-request.component.ts | 23 + .../metrics-system.component.html | 51 + .../metrics-system.component.ts | 43 + .../app/admin/metrics/metrics.component.html | 51 + .../admin/metrics/metrics.component.spec.ts | 41 + .../app/admin/metrics/metrics.component.ts | 40 + .../webapp/app/admin/metrics/metrics.model.ts | 159 + .../app/admin/metrics/metrics.module.ts | 32 + .../webapp/app/admin/metrics/metrics.route.ts | 11 + .../app/admin/metrics/metrics.service.spec.ts | 81 + .../app/admin/metrics/metrics.service.ts | 19 + ...er-management-delete-dialog.component.html | 25 + ...management-delete-dialog.component.spec.ts | 52 + ...user-management-delete-dialog.component.ts | 25 + .../user-management-detail.component.html | 56 + .../user-management-detail.component.spec.ts | 56 + .../user-management-detail.component.ts | 20 + .../list/user-management.component.html | 124 + .../list/user-management.component.spec.ts | 104 + .../list/user-management.component.ts | 111 + .../service/user-management.service.spec.ts | 67 + .../service/user-management.service.ts | 40 + .../user-management-update.component.html | 142 + .../user-management-update.component.spec.ts | 95 + .../user-management-update.component.ts | 88 + .../user-management/user-management.model.ts | 31 + .../user-management/user-management.module.ts | 20 + .../user-management/user-management.route.ts | 53 + src/main/webapp/app/app-routing.module.ts | 43 + src/main/webapp/app/app.constants.ts | 9 + src/main/webapp/app/app.module.ts | 58 + .../webapp/app/config/authority.constants.ts | 4 + .../webapp/app/config/datepicker-adapter.ts | 20 + src/main/webapp/app/config/dayjs.ts | 13 + src/main/webapp/app/config/error.constants.ts | 3 + .../webapp/app/config/font-awesome-icons.ts | 83 + src/main/webapp/app/config/input.constants.ts | 2 + .../webapp/app/config/language.constants.ts | 9 + .../webapp/app/config/navigation.constants.ts | 5 + .../webapp/app/config/pagination.constants.ts | 3 + .../webapp/app/config/translation.config.ts | 20 + .../app/config/uib-pagination.config.ts | 14 + .../webapp/app/core/auth/account.model.ts | 12 + .../app/core/auth/account.service.spec.ts | 251 + .../webapp/app/core/auth/account.service.ts | 92 + .../app/core/auth/auth-jwt.service.spec.ts | 88 + .../webapp/app/core/auth/auth-jwt.service.ts | 53 + .../app/core/auth/state-storage.service.ts | 21 + .../core/auth/user-route-access.service.ts | 36 + .../config/application-config.service.spec.ts | 40 + .../core/config/application-config.service.ts | 28 + .../interceptor/auth-expired.interceptor.ts | 33 + .../app/core/interceptor/auth.interceptor.ts | 33 + .../interceptor/error-handler.interceptor.ts | 23 + src/main/webapp/app/core/interceptor/index.ts | 29 + .../interceptor/notification.interceptor.ts | 38 + .../webapp/app/core/request/request-util.ts | 23 + .../webapp/app/core/request/request.model.ts | 11 + .../app/core/util/alert.service.spec.ts | 285 + .../webapp/app/core/util/alert.service.ts | 95 + .../app/core/util/data-util.service.spec.ts | 34 + .../webapp/app/core/util/data-util.service.ts | 129 + .../core/util/event-manager.service.spec.ts | 84 + .../app/core/util/event-manager.service.ts | 63 + .../webapp/app/core/util/operators.spec.ts | 18 + src/main/webapp/app/core/util/operators.ts | 9 + .../app/core/util/parse-links.service.spec.ts | 36 + .../app/core/util/parse-links.service.ts | 47 + .../blood-pressure/blood-pressure.model.ts | 12 + .../blood-pressure/blood-pressure.module.ts | 13 + .../blood-pressure.test-samples.ts | 36 + ...lood-pressure-delete-dialog.component.html | 28 + ...d-pressure-delete-dialog.component.spec.ts | 63 + .../blood-pressure-delete-dialog.component.ts | 25 + .../blood-pressure-detail.component.html | 46 + .../blood-pressure-detail.component.spec.ts | 36 + .../detail/blood-pressure-detail.component.ts | 24 + .../list/blood-pressure.component.html | 142 + .../list/blood-pressure.component.spec.ts | 124 + .../list/blood-pressure.component.ts | 185 + ...d-pressure-routing-resolve.service.spec.ts | 89 + .../blood-pressure-routing-resolve.service.ts | 30 + .../route/blood-pressure-routing.module.ts | 50 + .../service/blood-pressure.service.spec.ts | 206 + .../service/blood-pressure.service.ts | 135 + .../blood-pressure-form.service.spec.ts | 93 + .../update/blood-pressure-form.service.ts | 110 + .../blood-pressure-update.component.html | 119 + .../blood-pressure-update.component.spec.ts | 167 + .../update/blood-pressure-update.component.ts | 92 + .../app/entities/entity-navbar-items.ts | 22 + .../app/entities/entity-routing.module.ts | 31 + .../app/entities/enumerations/units.model.ts | 5 + .../points-delete-dialog.component.html | 24 + .../points-delete-dialog.component.spec.ts | 63 + .../delete/points-delete-dialog.component.ts | 25 + .../detail/points-detail.component.html | 52 + .../detail/points-detail.component.spec.ts | 36 + .../points/detail/points-detail.component.ts | 24 + .../points/list/points.component.html | 157 + .../points/list/points.component.spec.ts | 113 + .../entities/points/list/points.component.ts | 166 + .../app/entities/points/points.model.ts | 14 + .../app/entities/points/points.module.ts | 13 + .../entities/points/points.test-samples.ts | 34 + .../points-routing-resolve.service.spec.ts | 89 + .../route/points-routing-resolve.service.ts | 30 + .../points/route/points-routing.module.ts | 50 + .../points/service/points.service.spec.ts | 207 + .../entities/points/service/points.service.ts | 134 + .../points/update/points-form.service.spec.ts | 97 + .../points/update/points-form.service.ts | 78 + .../update/points-update.component.html | 94 + .../update/points-update.component.spec.ts | 167 + .../points/update/points-update.component.ts | 92 + .../preferences-delete-dialog.component.html | 28 + ...references-delete-dialog.component.spec.ts | 63 + .../preferences-delete-dialog.component.ts | 25 + .../detail/preferences-detail.component.html | 40 + .../preferences-detail.component.spec.ts | 36 + .../detail/preferences-detail.component.ts | 24 + .../list/preferences.component.html | 130 + .../list/preferences.component.spec.ts | 76 + .../preferences/list/preferences.component.ts | 143 + .../entities/preferences/preferences.model.ts | 11 + .../preferences/preferences.module.ts | 13 + .../preferences/preferences.test-samples.ts | 32 + ...references-routing-resolve.service.spec.ts | 89 + .../preferences-routing-resolve.service.ts | 30 + .../route/preferences-routing.module.ts | 50 + .../service/preferences.service.spec.ts | 205 + .../service/preferences.service.ts | 86 + .../update/preferences-form.service.spec.ts | 91 + .../update/preferences-form.service.ts | 72 + .../update/preferences-update.component.html | 107 + .../preferences-update.component.spec.ts | 167 + .../update/preferences-update.component.ts | 94 + .../webapp/app/entities/user/user.model.ts | 12 + .../app/entities/user/user.service.spec.ts | 109 + .../webapp/app/entities/user/user.service.ts | 45 + .../weight-delete-dialog.component.html | 24 + .../weight-delete-dialog.component.spec.ts | 63 + .../delete/weight-delete-dialog.component.ts | 25 + .../detail/weight-detail.component.html | 40 + .../detail/weight-detail.component.spec.ts | 36 + .../weight/detail/weight-detail.component.ts | 24 + .../weight/list/weight.component.html | 123 + .../weight/list/weight.component.spec.ts | 124 + .../entities/weight/list/weight.component.ts | 185 + .../weight-routing-resolve.service.spec.ts | 89 + .../route/weight-routing-resolve.service.ts | 30 + .../weight/route/weight-routing.module.ts | 50 + .../weight/service/weight.service.spec.ts | 206 + .../entities/weight/service/weight.service.ts | 133 + .../weight/update/weight-form.service.spec.ts | 91 + .../weight/update/weight-form.service.ts | 104 + .../update/weight-update.component.html | 86 + .../update/weight-update.component.spec.ts | 167 + .../weight/update/weight-update.component.ts | 92 + .../app/entities/weight/weight.model.ts | 11 + .../app/entities/weight/weight.module.ts | 13 + .../entities/weight/weight.test-samples.ts | 32 + src/main/webapp/app/home/home.component.html | 78 + src/main/webapp/app/home/home.component.scss | 23 + .../webapp/app/home/home.component.spec.ts | 112 + src/main/webapp/app/home/home.component.ts | 36 + src/main/webapp/app/home/home.module.ts | 12 + src/main/webapp/app/home/home.route.ts | 11 + .../app/layouts/error/error.component.html | 15 + .../app/layouts/error/error.component.ts | 41 + .../webapp/app/layouts/error/error.route.ts | 33 + .../app/layouts/footer/footer.component.html | 3 + .../app/layouts/footer/footer.component.ts | 7 + .../app/layouts/main/main.component.html | 13 + .../app/layouts/main/main.component.spec.ts | 196 + .../webapp/app/layouts/main/main.component.ts | 58 + .../layouts/navbar/active-menu.directive.ts | 27 + .../app/layouts/navbar/navbar.component.html | 252 + .../app/layouts/navbar/navbar.component.scss | 36 + .../layouts/navbar/navbar.component.spec.ts | 98 + .../app/layouts/navbar/navbar.component.ts | 75 + .../webapp/app/layouts/navbar/navbar.route.ts | 9 + .../profiles/page-ribbon.component.scss | 25 + .../profiles/page-ribbon.component.spec.ts | 40 + .../layouts/profiles/page-ribbon.component.ts | 24 + .../layouts/profiles/profile-info.model.ts | 15 + .../app/layouts/profiles/profile.service.ts | 41 + .../webapp/app/login/login.component.html | 57 + .../webapp/app/login/login.component.spec.ts | 153 + src/main/webapp/app/login/login.component.ts | 51 + src/main/webapp/app/login/login.model.ts | 3 + src/main/webapp/app/login/login.module.ts | 12 + src/main/webapp/app/login/login.route.ts | 11 + src/main/webapp/app/login/login.service.ts | 21 + .../shared/alert/alert-error.component.html | 7 + .../alert/alert-error.component.spec.ts | 160 + .../app/shared/alert/alert-error.component.ts | 108 + .../app/shared/alert/alert-error.model.ts | 3 + .../app/shared/alert/alert.component.html | 7 + .../app/shared/alert/alert.component.spec.ts | 44 + .../app/shared/alert/alert.component.ts | 33 + .../auth/has-any-authority.directive.spec.ts | 130 + .../auth/has-any-authority.directive.ts | 53 + .../webapp/app/shared/date/duration.pipe.ts | 15 + .../date/format-medium-date.pipe.spec.ts | 19 + .../shared/date/format-medium-date.pipe.ts | 12 + .../date/format-medium-datetime.pipe.spec.ts | 19 + .../date/format-medium-datetime.pipe.ts | 12 + .../app/shared/filter/filter.component.html | 12 + .../app/shared/filter/filter.component.ts | 18 + .../app/shared/filter/filter.model.spec.ts | 242 + .../webapp/app/shared/filter/filter.model.ts | 156 + .../language/find-language-from-key.pipe.ts | 14 + .../language/translate.directive.spec.ts | 35 + .../shared/language/translate.directive.ts | 51 + .../app/shared/language/translation.module.ts | 29 + .../pagination/item-count.component.spec.ts | 68 + .../shared/pagination/item-count.component.ts | 31 + .../webapp/app/shared/shared-libs.module.ts | 12 + src/main/webapp/app/shared/shared.module.ts | 49 + .../app/shared/sort/sort-by.directive.spec.ts | 139 + .../app/shared/sort/sort-by.directive.ts | 55 + .../app/shared/sort/sort.directive.spec.ts | 86 + .../webapp/app/shared/sort/sort.directive.ts | 39 + .../webapp/app/shared/sort/sort.service.ts | 13 + src/main/webapp/bootstrap.ts | 16 + src/main/webapp/content/css/loading.css | 152 + .../images/jhipster_family_member_0.svg | 1 + .../jhipster_family_member_0_head-192.png | Bin 0 -> 13439 bytes .../jhipster_family_member_0_head-256.png | Bin 0 -> 7037 bytes .../jhipster_family_member_0_head-384.png | Bin 0 -> 10350 bytes .../jhipster_family_member_0_head-512.png | Bin 0 -> 11431 bytes .../images/jhipster_family_member_1.svg | 1 + .../jhipster_family_member_1_head-192.png | Bin 0 -> 7046 bytes .../jhipster_family_member_1_head-256.png | Bin 0 -> 9505 bytes .../jhipster_family_member_1_head-384.png | Bin 0 -> 15054 bytes .../jhipster_family_member_1_head-512.png | Bin 0 -> 16456 bytes .../images/jhipster_family_member_2.svg | 1 + .../jhipster_family_member_2_head-192.png | Bin 0 -> 5423 bytes .../jhipster_family_member_2_head-256.png | Bin 0 -> 6687 bytes .../jhipster_family_member_2_head-384.png | Bin 0 -> 9682 bytes .../jhipster_family_member_2_head-512.png | Bin 0 -> 10514 bytes .../images/jhipster_family_member_3.svg | 1 + .../jhipster_family_member_3_head-192.png | Bin 0 -> 6148 bytes .../jhipster_family_member_3_head-256.png | Bin 0 -> 8028 bytes .../jhipster_family_member_3_head-384.png | Bin 0 -> 11998 bytes .../jhipster_family_member_3_head-512.png | Bin 0 -> 13555 bytes .../webapp/content/images/logo-jhipster.png | Bin 0 -> 605 bytes .../content/scss/_bootstrap-variables.scss | 45 + src/main/webapp/content/scss/global.scss | 239 + src/main/webapp/content/scss/vendor.scss | 12 + src/main/webapp/declarations.d.ts | 2 + src/main/webapp/favicon.ico | Bin 0 -> 1574 bytes src/main/webapp/i18n/en/activate.json | 9 + src/main/webapp/i18n/en/bloodPressure.json | 28 + src/main/webapp/i18n/en/configuration.json | 10 + .../webapp/i18n/en/elasticsearch-reindex.json | 18 + src/main/webapp/i18n/en/error.json | 14 + src/main/webapp/i18n/en/global.json | 152 + src/main/webapp/i18n/en/health.json | 33 + src/main/webapp/i18n/en/home.json | 19 + src/main/webapp/i18n/en/login.json | 19 + src/main/webapp/i18n/en/logs.json | 11 + src/main/webapp/i18n/en/metrics.json | 102 + src/main/webapp/i18n/en/password.json | 12 + src/main/webapp/i18n/en/points.json | 30 + src/main/webapp/i18n/en/preferences.json | 27 + src/main/webapp/i18n/en/register.json | 24 + src/main/webapp/i18n/en/reset.json | 26 + src/main/webapp/i18n/en/sessions.json | 15 + src/main/webapp/i18n/en/settings.json | 32 + src/main/webapp/i18n/en/units.json | 9 + src/main/webapp/i18n/en/user-management.json | 31 + src/main/webapp/i18n/en/weight.json | 27 + src/main/webapp/i18n/fr/activate.json | 9 + src/main/webapp/i18n/fr/bloodPressure.json | 28 + src/main/webapp/i18n/fr/configuration.json | 10 + .../webapp/i18n/fr/elasticsearch-reindex.json | 18 + src/main/webapp/i18n/fr/error.json | 14 + src/main/webapp/i18n/fr/global.json | 152 + src/main/webapp/i18n/fr/health.json | 32 + src/main/webapp/i18n/fr/home.json | 19 + src/main/webapp/i18n/fr/login.json | 19 + src/main/webapp/i18n/fr/logs.json | 11 + src/main/webapp/i18n/fr/metrics.json | 102 + src/main/webapp/i18n/fr/password.json | 12 + src/main/webapp/i18n/fr/points.json | 30 + src/main/webapp/i18n/fr/preferences.json | 27 + src/main/webapp/i18n/fr/register.json | 24 + src/main/webapp/i18n/fr/reset.json | 26 + src/main/webapp/i18n/fr/sessions.json | 15 + src/main/webapp/i18n/fr/settings.json | 32 + src/main/webapp/i18n/fr/units.json | 9 + src/main/webapp/i18n/fr/user-management.json | 31 + src/main/webapp/i18n/fr/weight.json | 27 + src/main/webapp/index.html | 128 + src/main/webapp/main.ts | 1 + src/main/webapp/manifest.webapp | 31 + src/main/webapp/polyfills.ts | 5 + src/main/webapp/robots.txt | 10 + .../swagger-ui/dist/images/throbber.gif | Bin 0 -> 9257 bytes src/main/webapp/swagger-ui/index.html | 92 + .../org/jhipster/health/IntegrationTest.java | 24 + .../health/TechnicalStructureTest.java | 37 + .../health/config/AsyncSyncConfiguration.java | 16 + .../ElasticsearchTestConfiguration.java | 19 + .../config/ElasticsearchTestContainer.java | 49 + .../health/config/EmbeddedElasticsearch.java | 11 + .../jhipster/health/config/EmbeddedSQL.java | 11 + .../config/PostgreSqlTestContainer.java | 42 + .../config/SpringBootTestClassOrderer.java | 22 + .../health/config/SqlTestContainer.java | 9 + .../StaticResourcesWebConfigurerTest.java | 76 + ...tainersSpringContextCustomizerFactory.java | 75 + .../health/config/WebConfigurerTest.java | 133 + .../config/WebConfigurerTestController.java | 14 + .../config/timezone/HibernateTimeZoneIT.java | 162 + .../health/domain/BloodPressureTest.java | 23 + .../jhipster/health/domain/PointsTest.java | 23 + .../health/domain/PreferencesTest.java | 23 + .../jhipster/health/domain/WeightTest.java | 23 + .../SecurityMetersServiceTests.java | 70 + .../repository/timezone/DateTimeWrapper.java | 133 + .../timezone/DateTimeWrapperRepository.java | 10 + .../security/DomainUserDetailsServiceIT.java | 113 + .../security/SecurityUtilsUnitTest.java | 101 + .../health/security/jwt/JWTFilterTest.java | 117 + .../jwt/TokenProviderSecurityMetersTests.java | 158 + .../security/jwt/TokenProviderTest.java | 141 + .../health/service/MailServiceIT.java | 237 + .../health/service/UserServiceIT.java | 204 + .../health/service/mapper/UserMapperTest.java | 132 + .../health/web/rest/AccountResourceIT.java | 762 + .../web/rest/BloodPressureResourceIT.java | 568 + .../web/rest/ClientForwardControllerTest.java | 68 + .../health/web/rest/PointsResourceIT.java | 554 + .../web/rest/PreferencesResourceIT.java | 528 + .../health/web/rest/PublicUserResourceIT.java | 108 + .../jhipster/health/web/rest/TestUtil.java | 206 + .../health/web/rest/UserJWTControllerIT.java | 98 + .../health/web/rest/UserResourceIT.java | 614 + .../health/web/rest/WeightResourceIT.java | 529 + .../web/rest/WithUnauthenticatedMockUser.java | 23 + .../rest/errors/ExceptionTranslatorIT.java | 118 + .../ExceptionTranslatorTestController.java | 66 + src/test/javascript/cypress/.eslintrc.json | 12 + .../cypress/e2e/account/login-page.cy.ts | 56 + .../cypress/e2e/account/password-page.cy.ts | 61 + .../cypress/e2e/account/register-page.cy.ts | 67 + .../e2e/account/reset-password-page.cy.ts | 34 + .../cypress/e2e/account/settings-page.cy.ts | 66 + .../e2e/administration/administration.cy.ts | 71 + .../cypress/e2e/entity/blood-pressure.cy.ts | 181 + .../cypress/e2e/entity/points.cy.ts | 185 + .../cypress/e2e/entity/preferences.cy.ts | 176 + .../cypress/e2e/entity/weight.cy.ts | 179 + .../cypress/fixtures/integration-test.png | Bin 0 -> 1085 bytes src/test/javascript/cypress/plugins/index.ts | 23 + .../javascript/cypress/support/commands.ts | 126 + src/test/javascript/cypress/support/entity.ts | 87 + src/test/javascript/cypress/support/index.ts | 19 + .../javascript/cypress/support/management.ts | 22 + src/test/javascript/cypress/support/navbar.ts | 59 + src/test/javascript/cypress/tsconfig.json | 10 + src/test/resources/META-INF/spring.factories | 1 + .../resources/config/application-testdev.yml | 31 + .../resources/config/application-testprod.yml | 34 + src/test/resources/config/application.yml | 92 + src/test/resources/config/bootstrap.yml | 0 .../resources/i18n/messages_en.properties | 4 + .../resources/i18n/messages_fr.properties | 4 + src/test/resources/junit-platform.properties | 4 + src/test/resources/logback.xml | 53 + .../templates/mail/activationEmail.html | 19 + .../templates/mail/creationEmail.html | 19 + .../templates/mail/passwordResetEmail.html | 21 + .../resources/templates/mail/testEmail.html | 1 + src/test/resources/testcontainers.properties | 1 + tsconfig.app.json | 9 + tsconfig.json | 28 + tsconfig.spec.json | 8 + webpack/environment.js | 6 + webpack/logo-jhipster.png | Bin 0 -> 3326 bytes webpack/proxy.conf.js | 13 + webpack/webpack.custom.js | 140 + 662 files changed, 88600 insertions(+) create mode 100644 .browserslistrc create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/devcontainer.json create mode 100644 .editorconfig create mode 100644 .eslintignore create mode 100644 .eslintrc.json create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100755 .husky/pre-commit create mode 100644 .jhipster/BloodPressure.json create mode 100644 .jhipster/Points.json create mode 100644 .jhipster/Preferences.json create mode 100644 .jhipster/Weight.json create mode 100644 .jhipster/modules/jhi-hooks.json create mode 100644 .lintstagedrc.js create mode 100644 .prettierignore create mode 100644 .prettierrc create mode 100644 .yo-rc.json create mode 100644 README.md create mode 100644 angular.json create mode 100644 build.gradle create mode 100644 checkstyle.xml create mode 100644 cypress.config.ts create mode 100644 gradle.properties create mode 100644 gradle/docker.gradle create mode 100644 gradle/profile_dev.gradle create mode 100644 gradle/profile_prod.gradle create mode 100644 gradle/sonar.gradle create mode 100644 gradle/war.gradle create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 gradle/zipkin.gradle create mode 100755 gradlew create mode 100644 gradlew.bat create mode 100644 jest.conf.js create mode 100644 ngsw-config.json create mode 100755 npmw create mode 100644 npmw.cmd create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 settings.gradle create mode 100644 sonar-project.properties create mode 100644 src/main/docker/app.yml create mode 100644 src/main/docker/central-server-config/README.md create mode 100644 src/main/docker/elasticsearch.yml create mode 100644 src/main/docker/grafana/provisioning/dashboards/JVM.json create mode 100644 src/main/docker/grafana/provisioning/dashboards/dashboard.yml create mode 100644 src/main/docker/grafana/provisioning/datasources/datasource.yml create mode 100644 src/main/docker/jhipster-control-center.yml create mode 100644 src/main/docker/jib/entrypoint.sh create mode 100644 src/main/docker/monitoring.yml create mode 100644 src/main/docker/postgresql.yml create mode 100644 src/main/docker/prometheus/prometheus.yml create mode 100644 src/main/docker/sonar.yml create mode 100644 src/main/docker/zipkin.yml create mode 100644 src/main/java/org/jhipster/health/ApplicationWebXml.java create mode 100644 src/main/java/org/jhipster/health/GeneratedByJHipster.java create mode 100644 src/main/java/org/jhipster/health/TwentyOnePointsApp.java create mode 100644 src/main/java/org/jhipster/health/aop/logging/LoggingAspect.java create mode 100644 src/main/java/org/jhipster/health/config/ApplicationProperties.java create mode 100644 src/main/java/org/jhipster/health/config/AsyncConfiguration.java create mode 100644 src/main/java/org/jhipster/health/config/CRLFLogConverter.java create mode 100644 src/main/java/org/jhipster/health/config/CacheConfiguration.java create mode 100644 src/main/java/org/jhipster/health/config/Constants.java create mode 100644 src/main/java/org/jhipster/health/config/DatabaseConfiguration.java create mode 100644 src/main/java/org/jhipster/health/config/DateTimeFormatConfiguration.java create mode 100644 src/main/java/org/jhipster/health/config/ElasticsearchConfiguration.java create mode 100644 src/main/java/org/jhipster/health/config/JacksonConfiguration.java create mode 100644 src/main/java/org/jhipster/health/config/LiquibaseConfiguration.java create mode 100644 src/main/java/org/jhipster/health/config/LocaleConfiguration.java create mode 100644 src/main/java/org/jhipster/health/config/LoggingAspectConfiguration.java create mode 100644 src/main/java/org/jhipster/health/config/LoggingConfiguration.java create mode 100644 src/main/java/org/jhipster/health/config/SecurityConfiguration.java create mode 100644 src/main/java/org/jhipster/health/config/StaticResourcesWebConfiguration.java create mode 100644 src/main/java/org/jhipster/health/config/WebConfigurer.java create mode 100644 src/main/java/org/jhipster/health/config/package-info.java create mode 100644 src/main/java/org/jhipster/health/domain/AbstractAuditingEntity.java create mode 100644 src/main/java/org/jhipster/health/domain/Authority.java create mode 100644 src/main/java/org/jhipster/health/domain/BloodPressure.java create mode 100644 src/main/java/org/jhipster/health/domain/Points.java create mode 100644 src/main/java/org/jhipster/health/domain/Preferences.java create mode 100644 src/main/java/org/jhipster/health/domain/User.java create mode 100644 src/main/java/org/jhipster/health/domain/Weight.java create mode 100644 src/main/java/org/jhipster/health/domain/enumeration/Units.java create mode 100644 src/main/java/org/jhipster/health/domain/package-info.java create mode 100644 src/main/java/org/jhipster/health/management/SecurityMetersService.java create mode 100644 src/main/java/org/jhipster/health/repository/AuthorityRepository.java create mode 100644 src/main/java/org/jhipster/health/repository/BloodPressureRepository.java create mode 100644 src/main/java/org/jhipster/health/repository/PointsRepository.java create mode 100644 src/main/java/org/jhipster/health/repository/PreferencesRepository.java create mode 100644 src/main/java/org/jhipster/health/repository/UserRepository.java create mode 100644 src/main/java/org/jhipster/health/repository/WeightRepository.java create mode 100644 src/main/java/org/jhipster/health/repository/package-info.java create mode 100644 src/main/java/org/jhipster/health/repository/search/BloodPressureSearchRepository.java create mode 100644 src/main/java/org/jhipster/health/repository/search/PointsSearchRepository.java create mode 100644 src/main/java/org/jhipster/health/repository/search/PreferencesSearchRepository.java create mode 100644 src/main/java/org/jhipster/health/repository/search/UserSearchRepository.java create mode 100644 src/main/java/org/jhipster/health/repository/search/WeightSearchRepository.java create mode 100644 src/main/java/org/jhipster/health/repository/search/package-info.java create mode 100644 src/main/java/org/jhipster/health/security/AuthoritiesConstants.java create mode 100644 src/main/java/org/jhipster/health/security/DomainUserDetailsService.java create mode 100644 src/main/java/org/jhipster/health/security/SecurityUtils.java create mode 100644 src/main/java/org/jhipster/health/security/SpringSecurityAuditorAware.java create mode 100644 src/main/java/org/jhipster/health/security/UserNotActivatedException.java create mode 100644 src/main/java/org/jhipster/health/security/jwt/JWTConfigurer.java create mode 100644 src/main/java/org/jhipster/health/security/jwt/JWTFilter.java create mode 100644 src/main/java/org/jhipster/health/security/jwt/TokenProvider.java create mode 100644 src/main/java/org/jhipster/health/security/package-info.java create mode 100644 src/main/java/org/jhipster/health/service/ElasticsearchIndexService.java create mode 100644 src/main/java/org/jhipster/health/service/EmailAlreadyUsedException.java create mode 100644 src/main/java/org/jhipster/health/service/InvalidPasswordException.java create mode 100644 src/main/java/org/jhipster/health/service/MailService.java create mode 100644 src/main/java/org/jhipster/health/service/UserService.java create mode 100644 src/main/java/org/jhipster/health/service/UsernameAlreadyUsedException.java create mode 100644 src/main/java/org/jhipster/health/service/dto/AdminUserDTO.java create mode 100644 src/main/java/org/jhipster/health/service/dto/PasswordChangeDTO.java create mode 100644 src/main/java/org/jhipster/health/service/dto/UserDTO.java create mode 100644 src/main/java/org/jhipster/health/service/dto/package-info.java create mode 100644 src/main/java/org/jhipster/health/service/mapper/UserMapper.java create mode 100644 src/main/java/org/jhipster/health/service/mapper/package-info.java create mode 100644 src/main/java/org/jhipster/health/service/package-info.java create mode 100644 src/main/java/org/jhipster/health/web/rest/AccountResource.java create mode 100644 src/main/java/org/jhipster/health/web/rest/BloodPressureResource.java create mode 100644 src/main/java/org/jhipster/health/web/rest/ClientForwardController.java create mode 100644 src/main/java/org/jhipster/health/web/rest/ElasticsearchIndexResource.java create mode 100644 src/main/java/org/jhipster/health/web/rest/PointsResource.java create mode 100644 src/main/java/org/jhipster/health/web/rest/PreferencesResource.java create mode 100644 src/main/java/org/jhipster/health/web/rest/PublicUserResource.java create mode 100644 src/main/java/org/jhipster/health/web/rest/UserJWTController.java create mode 100644 src/main/java/org/jhipster/health/web/rest/UserResource.java create mode 100644 src/main/java/org/jhipster/health/web/rest/WeightResource.java create mode 100644 src/main/java/org/jhipster/health/web/rest/errors/BadRequestAlertException.java create mode 100644 src/main/java/org/jhipster/health/web/rest/errors/EmailAlreadyUsedException.java create mode 100644 src/main/java/org/jhipster/health/web/rest/errors/ErrorConstants.java create mode 100644 src/main/java/org/jhipster/health/web/rest/errors/ExceptionTranslator.java create mode 100644 src/main/java/org/jhipster/health/web/rest/errors/FieldErrorVM.java create mode 100644 src/main/java/org/jhipster/health/web/rest/errors/InvalidPasswordException.java create mode 100644 src/main/java/org/jhipster/health/web/rest/errors/LoginAlreadyUsedException.java create mode 100644 src/main/java/org/jhipster/health/web/rest/errors/package-info.java create mode 100644 src/main/java/org/jhipster/health/web/rest/package-info.java create mode 100644 src/main/java/org/jhipster/health/web/rest/vm/KeyAndPasswordVM.java create mode 100644 src/main/java/org/jhipster/health/web/rest/vm/LoginVM.java create mode 100644 src/main/java/org/jhipster/health/web/rest/vm/ManagedUserVM.java create mode 100644 src/main/java/org/jhipster/health/web/rest/vm/package-info.java create mode 100644 src/main/resources/.h2.server.properties create mode 100644 src/main/resources/banner.txt create mode 100644 src/main/resources/config/application-dev.yml create mode 100644 src/main/resources/config/application-prod.yml create mode 100644 src/main/resources/config/application-tls.yml create mode 100644 src/main/resources/config/application.yml create mode 100644 src/main/resources/config/bootstrap-prod.yml create mode 100644 src/main/resources/config/bootstrap.yml create mode 100644 src/main/resources/config/liquibase/changelog/00000000000000_initial_schema.xml create mode 100644 src/main/resources/config/liquibase/changelog/20221108000520_added_entity_Points.xml create mode 100644 src/main/resources/config/liquibase/changelog/20221108000520_added_entity_constraints_Points.xml create mode 100644 src/main/resources/config/liquibase/changelog/20221108001452_added_entity_Weight.xml create mode 100644 src/main/resources/config/liquibase/changelog/20221108001452_added_entity_constraints_Weight.xml create mode 100644 src/main/resources/config/liquibase/changelog/20221108001642_added_entity_BloodPressure.xml create mode 100644 src/main/resources/config/liquibase/changelog/20221108001642_added_entity_constraints_BloodPressure.xml create mode 100644 src/main/resources/config/liquibase/changelog/20221108002021_added_entity_Preferences.xml create mode 100644 src/main/resources/config/liquibase/changelog/20221108002021_added_entity_constraints_Preferences.xml create mode 100644 src/main/resources/config/liquibase/data/authority.csv create mode 100644 src/main/resources/config/liquibase/data/user.csv create mode 100644 src/main/resources/config/liquibase/data/user_authority.csv create mode 100644 src/main/resources/config/liquibase/fake-data/blood_pressure.csv create mode 100644 src/main/resources/config/liquibase/fake-data/points.csv create mode 100644 src/main/resources/config/liquibase/fake-data/preferences.csv create mode 100644 src/main/resources/config/liquibase/fake-data/weight.csv create mode 100644 src/main/resources/config/liquibase/master.xml create mode 100644 src/main/resources/i18n/messages.properties create mode 100644 src/main/resources/i18n/messages_en.properties create mode 100644 src/main/resources/i18n/messages_fr.properties create mode 100644 src/main/resources/logback-spring.xml create mode 100644 src/main/resources/templates/error.html create mode 100644 src/main/resources/templates/mail/activationEmail.html create mode 100644 src/main/resources/templates/mail/creationEmail.html create mode 100644 src/main/resources/templates/mail/passwordResetEmail.html create mode 100644 src/main/webapp/404.html create mode 100644 src/main/webapp/WEB-INF/web.xml create mode 100644 src/main/webapp/app/account/account.module.ts create mode 100644 src/main/webapp/app/account/account.route.ts create mode 100644 src/main/webapp/app/account/activate/activate.component.html create mode 100644 src/main/webapp/app/account/activate/activate.component.spec.ts create mode 100644 src/main/webapp/app/account/activate/activate.component.ts create mode 100644 src/main/webapp/app/account/activate/activate.route.ts create mode 100644 src/main/webapp/app/account/activate/activate.service.spec.ts create mode 100644 src/main/webapp/app/account/activate/activate.service.ts create mode 100644 src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.html create mode 100644 src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.spec.ts create mode 100644 src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.ts create mode 100644 src/main/webapp/app/account/password-reset/finish/password-reset-finish.route.ts create mode 100644 src/main/webapp/app/account/password-reset/finish/password-reset-finish.service.spec.ts create mode 100644 src/main/webapp/app/account/password-reset/finish/password-reset-finish.service.ts create mode 100644 src/main/webapp/app/account/password-reset/init/password-reset-init.component.html create mode 100644 src/main/webapp/app/account/password-reset/init/password-reset-init.component.spec.ts create mode 100644 src/main/webapp/app/account/password-reset/init/password-reset-init.component.ts create mode 100644 src/main/webapp/app/account/password-reset/init/password-reset-init.route.ts create mode 100644 src/main/webapp/app/account/password-reset/init/password-reset-init.service.spec.ts create mode 100644 src/main/webapp/app/account/password-reset/init/password-reset-init.service.ts create mode 100644 src/main/webapp/app/account/password/password-strength-bar/password-strength-bar.component.html create mode 100644 src/main/webapp/app/account/password/password-strength-bar/password-strength-bar.component.scss create mode 100644 src/main/webapp/app/account/password/password-strength-bar/password-strength-bar.component.spec.ts create mode 100644 src/main/webapp/app/account/password/password-strength-bar/password-strength-bar.component.ts create mode 100644 src/main/webapp/app/account/password/password.component.html create mode 100644 src/main/webapp/app/account/password/password.component.spec.ts create mode 100644 src/main/webapp/app/account/password/password.component.ts create mode 100644 src/main/webapp/app/account/password/password.route.ts create mode 100644 src/main/webapp/app/account/password/password.service.spec.ts create mode 100644 src/main/webapp/app/account/password/password.service.ts create mode 100644 src/main/webapp/app/account/register/register.component.html create mode 100644 src/main/webapp/app/account/register/register.component.spec.ts create mode 100644 src/main/webapp/app/account/register/register.component.ts create mode 100644 src/main/webapp/app/account/register/register.model.ts create mode 100644 src/main/webapp/app/account/register/register.route.ts create mode 100644 src/main/webapp/app/account/register/register.service.spec.ts create mode 100644 src/main/webapp/app/account/register/register.service.ts create mode 100644 src/main/webapp/app/account/settings/settings.component.html create mode 100644 src/main/webapp/app/account/settings/settings.component.spec.ts create mode 100644 src/main/webapp/app/account/settings/settings.component.ts create mode 100644 src/main/webapp/app/account/settings/settings.route.ts create mode 100644 src/main/webapp/app/admin/admin-routing.module.ts create mode 100644 src/main/webapp/app/admin/configuration/configuration.component.html create mode 100644 src/main/webapp/app/admin/configuration/configuration.component.spec.ts create mode 100644 src/main/webapp/app/admin/configuration/configuration.component.ts create mode 100644 src/main/webapp/app/admin/configuration/configuration.model.ts create mode 100644 src/main/webapp/app/admin/configuration/configuration.module.ts create mode 100644 src/main/webapp/app/admin/configuration/configuration.route.ts create mode 100644 src/main/webapp/app/admin/configuration/configuration.service.spec.ts create mode 100644 src/main/webapp/app/admin/configuration/configuration.service.ts create mode 100644 src/main/webapp/app/admin/docs/docs.component.html create mode 100644 src/main/webapp/app/admin/docs/docs.component.scss create mode 100644 src/main/webapp/app/admin/docs/docs.component.ts create mode 100644 src/main/webapp/app/admin/docs/docs.module.ts create mode 100644 src/main/webapp/app/admin/docs/docs.route.ts create mode 100644 src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex-modal.component.html create mode 100644 src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex-modal.component.ts create mode 100644 src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex-selected-modal.component.ts create mode 100644 src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex.component.html create mode 100644 src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex.component.ts create mode 100644 src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex.module.ts create mode 100644 src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex.route.ts create mode 100644 src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex.service.ts create mode 100644 src/main/webapp/app/admin/elasticsearch-reindex/index.ts create mode 100644 src/main/webapp/app/admin/health/health.component.html create mode 100644 src/main/webapp/app/admin/health/health.component.spec.ts create mode 100644 src/main/webapp/app/admin/health/health.component.ts create mode 100644 src/main/webapp/app/admin/health/health.model.ts create mode 100644 src/main/webapp/app/admin/health/health.module.ts create mode 100644 src/main/webapp/app/admin/health/health.route.ts create mode 100644 src/main/webapp/app/admin/health/health.service.spec.ts create mode 100644 src/main/webapp/app/admin/health/health.service.ts create mode 100644 src/main/webapp/app/admin/health/modal/health-modal.component.html create mode 100644 src/main/webapp/app/admin/health/modal/health-modal.component.spec.ts create mode 100644 src/main/webapp/app/admin/health/modal/health-modal.component.ts create mode 100644 src/main/webapp/app/admin/logs/log.model.ts create mode 100644 src/main/webapp/app/admin/logs/logs.component.html create mode 100644 src/main/webapp/app/admin/logs/logs.component.spec.ts create mode 100644 src/main/webapp/app/admin/logs/logs.component.ts create mode 100644 src/main/webapp/app/admin/logs/logs.module.ts create mode 100644 src/main/webapp/app/admin/logs/logs.route.ts create mode 100644 src/main/webapp/app/admin/logs/logs.service.spec.ts create mode 100644 src/main/webapp/app/admin/logs/logs.service.ts create mode 100644 src/main/webapp/app/admin/metrics/blocks/jvm-memory/jvm-memory.component.html create mode 100644 src/main/webapp/app/admin/metrics/blocks/jvm-memory/jvm-memory.component.ts create mode 100644 src/main/webapp/app/admin/metrics/blocks/jvm-threads/jvm-threads.component.html create mode 100644 src/main/webapp/app/admin/metrics/blocks/jvm-threads/jvm-threads.component.ts create mode 100644 src/main/webapp/app/admin/metrics/blocks/metrics-cache/metrics-cache.component.html create mode 100644 src/main/webapp/app/admin/metrics/blocks/metrics-cache/metrics-cache.component.ts create mode 100644 src/main/webapp/app/admin/metrics/blocks/metrics-datasource/metrics-datasource.component.html create mode 100644 src/main/webapp/app/admin/metrics/blocks/metrics-datasource/metrics-datasource.component.ts create mode 100644 src/main/webapp/app/admin/metrics/blocks/metrics-endpoints-requests/metrics-endpoints-requests.component.html create mode 100644 src/main/webapp/app/admin/metrics/blocks/metrics-endpoints-requests/metrics-endpoints-requests.component.ts create mode 100644 src/main/webapp/app/admin/metrics/blocks/metrics-garbagecollector/metrics-garbagecollector.component.html create mode 100644 src/main/webapp/app/admin/metrics/blocks/metrics-garbagecollector/metrics-garbagecollector.component.ts create mode 100644 src/main/webapp/app/admin/metrics/blocks/metrics-modal-threads/metrics-modal-threads.component.html create mode 100644 src/main/webapp/app/admin/metrics/blocks/metrics-modal-threads/metrics-modal-threads.component.ts create mode 100644 src/main/webapp/app/admin/metrics/blocks/metrics-request/metrics-request.component.html create mode 100644 src/main/webapp/app/admin/metrics/blocks/metrics-request/metrics-request.component.ts create mode 100644 src/main/webapp/app/admin/metrics/blocks/metrics-system/metrics-system.component.html create mode 100644 src/main/webapp/app/admin/metrics/blocks/metrics-system/metrics-system.component.ts create mode 100644 src/main/webapp/app/admin/metrics/metrics.component.html create mode 100644 src/main/webapp/app/admin/metrics/metrics.component.spec.ts create mode 100644 src/main/webapp/app/admin/metrics/metrics.component.ts create mode 100644 src/main/webapp/app/admin/metrics/metrics.model.ts create mode 100644 src/main/webapp/app/admin/metrics/metrics.module.ts create mode 100644 src/main/webapp/app/admin/metrics/metrics.route.ts create mode 100644 src/main/webapp/app/admin/metrics/metrics.service.spec.ts create mode 100644 src/main/webapp/app/admin/metrics/metrics.service.ts create mode 100644 src/main/webapp/app/admin/user-management/delete/user-management-delete-dialog.component.html create mode 100644 src/main/webapp/app/admin/user-management/delete/user-management-delete-dialog.component.spec.ts create mode 100644 src/main/webapp/app/admin/user-management/delete/user-management-delete-dialog.component.ts create mode 100644 src/main/webapp/app/admin/user-management/detail/user-management-detail.component.html create mode 100644 src/main/webapp/app/admin/user-management/detail/user-management-detail.component.spec.ts create mode 100644 src/main/webapp/app/admin/user-management/detail/user-management-detail.component.ts create mode 100644 src/main/webapp/app/admin/user-management/list/user-management.component.html create mode 100644 src/main/webapp/app/admin/user-management/list/user-management.component.spec.ts create mode 100644 src/main/webapp/app/admin/user-management/list/user-management.component.ts create mode 100644 src/main/webapp/app/admin/user-management/service/user-management.service.spec.ts create mode 100644 src/main/webapp/app/admin/user-management/service/user-management.service.ts create mode 100644 src/main/webapp/app/admin/user-management/update/user-management-update.component.html create mode 100644 src/main/webapp/app/admin/user-management/update/user-management-update.component.spec.ts create mode 100644 src/main/webapp/app/admin/user-management/update/user-management-update.component.ts create mode 100644 src/main/webapp/app/admin/user-management/user-management.model.ts create mode 100644 src/main/webapp/app/admin/user-management/user-management.module.ts create mode 100644 src/main/webapp/app/admin/user-management/user-management.route.ts create mode 100644 src/main/webapp/app/app-routing.module.ts create mode 100644 src/main/webapp/app/app.constants.ts create mode 100644 src/main/webapp/app/app.module.ts create mode 100644 src/main/webapp/app/config/authority.constants.ts create mode 100644 src/main/webapp/app/config/datepicker-adapter.ts create mode 100644 src/main/webapp/app/config/dayjs.ts create mode 100644 src/main/webapp/app/config/error.constants.ts create mode 100644 src/main/webapp/app/config/font-awesome-icons.ts create mode 100644 src/main/webapp/app/config/input.constants.ts create mode 100644 src/main/webapp/app/config/language.constants.ts create mode 100644 src/main/webapp/app/config/navigation.constants.ts create mode 100644 src/main/webapp/app/config/pagination.constants.ts create mode 100644 src/main/webapp/app/config/translation.config.ts create mode 100644 src/main/webapp/app/config/uib-pagination.config.ts create mode 100644 src/main/webapp/app/core/auth/account.model.ts create mode 100644 src/main/webapp/app/core/auth/account.service.spec.ts create mode 100644 src/main/webapp/app/core/auth/account.service.ts create mode 100644 src/main/webapp/app/core/auth/auth-jwt.service.spec.ts create mode 100644 src/main/webapp/app/core/auth/auth-jwt.service.ts create mode 100644 src/main/webapp/app/core/auth/state-storage.service.ts create mode 100644 src/main/webapp/app/core/auth/user-route-access.service.ts create mode 100644 src/main/webapp/app/core/config/application-config.service.spec.ts create mode 100644 src/main/webapp/app/core/config/application-config.service.ts create mode 100644 src/main/webapp/app/core/interceptor/auth-expired.interceptor.ts create mode 100644 src/main/webapp/app/core/interceptor/auth.interceptor.ts create mode 100644 src/main/webapp/app/core/interceptor/error-handler.interceptor.ts create mode 100644 src/main/webapp/app/core/interceptor/index.ts create mode 100644 src/main/webapp/app/core/interceptor/notification.interceptor.ts create mode 100644 src/main/webapp/app/core/request/request-util.ts create mode 100644 src/main/webapp/app/core/request/request.model.ts create mode 100644 src/main/webapp/app/core/util/alert.service.spec.ts create mode 100644 src/main/webapp/app/core/util/alert.service.ts create mode 100644 src/main/webapp/app/core/util/data-util.service.spec.ts create mode 100644 src/main/webapp/app/core/util/data-util.service.ts create mode 100644 src/main/webapp/app/core/util/event-manager.service.spec.ts create mode 100644 src/main/webapp/app/core/util/event-manager.service.ts create mode 100644 src/main/webapp/app/core/util/operators.spec.ts create mode 100644 src/main/webapp/app/core/util/operators.ts create mode 100644 src/main/webapp/app/core/util/parse-links.service.spec.ts create mode 100644 src/main/webapp/app/core/util/parse-links.service.ts create mode 100644 src/main/webapp/app/entities/blood-pressure/blood-pressure.model.ts create mode 100644 src/main/webapp/app/entities/blood-pressure/blood-pressure.module.ts create mode 100644 src/main/webapp/app/entities/blood-pressure/blood-pressure.test-samples.ts create mode 100644 src/main/webapp/app/entities/blood-pressure/delete/blood-pressure-delete-dialog.component.html create mode 100644 src/main/webapp/app/entities/blood-pressure/delete/blood-pressure-delete-dialog.component.spec.ts create mode 100644 src/main/webapp/app/entities/blood-pressure/delete/blood-pressure-delete-dialog.component.ts create mode 100644 src/main/webapp/app/entities/blood-pressure/detail/blood-pressure-detail.component.html create mode 100644 src/main/webapp/app/entities/blood-pressure/detail/blood-pressure-detail.component.spec.ts create mode 100644 src/main/webapp/app/entities/blood-pressure/detail/blood-pressure-detail.component.ts create mode 100644 src/main/webapp/app/entities/blood-pressure/list/blood-pressure.component.html create mode 100644 src/main/webapp/app/entities/blood-pressure/list/blood-pressure.component.spec.ts create mode 100644 src/main/webapp/app/entities/blood-pressure/list/blood-pressure.component.ts create mode 100644 src/main/webapp/app/entities/blood-pressure/route/blood-pressure-routing-resolve.service.spec.ts create mode 100644 src/main/webapp/app/entities/blood-pressure/route/blood-pressure-routing-resolve.service.ts create mode 100644 src/main/webapp/app/entities/blood-pressure/route/blood-pressure-routing.module.ts create mode 100644 src/main/webapp/app/entities/blood-pressure/service/blood-pressure.service.spec.ts create mode 100644 src/main/webapp/app/entities/blood-pressure/service/blood-pressure.service.ts create mode 100644 src/main/webapp/app/entities/blood-pressure/update/blood-pressure-form.service.spec.ts create mode 100644 src/main/webapp/app/entities/blood-pressure/update/blood-pressure-form.service.ts create mode 100644 src/main/webapp/app/entities/blood-pressure/update/blood-pressure-update.component.html create mode 100644 src/main/webapp/app/entities/blood-pressure/update/blood-pressure-update.component.spec.ts create mode 100644 src/main/webapp/app/entities/blood-pressure/update/blood-pressure-update.component.ts create mode 100644 src/main/webapp/app/entities/entity-navbar-items.ts create mode 100644 src/main/webapp/app/entities/entity-routing.module.ts create mode 100644 src/main/webapp/app/entities/enumerations/units.model.ts create mode 100644 src/main/webapp/app/entities/points/delete/points-delete-dialog.component.html create mode 100644 src/main/webapp/app/entities/points/delete/points-delete-dialog.component.spec.ts create mode 100644 src/main/webapp/app/entities/points/delete/points-delete-dialog.component.ts create mode 100644 src/main/webapp/app/entities/points/detail/points-detail.component.html create mode 100644 src/main/webapp/app/entities/points/detail/points-detail.component.spec.ts create mode 100644 src/main/webapp/app/entities/points/detail/points-detail.component.ts create mode 100644 src/main/webapp/app/entities/points/list/points.component.html create mode 100644 src/main/webapp/app/entities/points/list/points.component.spec.ts create mode 100644 src/main/webapp/app/entities/points/list/points.component.ts create mode 100644 src/main/webapp/app/entities/points/points.model.ts create mode 100644 src/main/webapp/app/entities/points/points.module.ts create mode 100644 src/main/webapp/app/entities/points/points.test-samples.ts create mode 100644 src/main/webapp/app/entities/points/route/points-routing-resolve.service.spec.ts create mode 100644 src/main/webapp/app/entities/points/route/points-routing-resolve.service.ts create mode 100644 src/main/webapp/app/entities/points/route/points-routing.module.ts create mode 100644 src/main/webapp/app/entities/points/service/points.service.spec.ts create mode 100644 src/main/webapp/app/entities/points/service/points.service.ts create mode 100644 src/main/webapp/app/entities/points/update/points-form.service.spec.ts create mode 100644 src/main/webapp/app/entities/points/update/points-form.service.ts create mode 100644 src/main/webapp/app/entities/points/update/points-update.component.html create mode 100644 src/main/webapp/app/entities/points/update/points-update.component.spec.ts create mode 100644 src/main/webapp/app/entities/points/update/points-update.component.ts create mode 100644 src/main/webapp/app/entities/preferences/delete/preferences-delete-dialog.component.html create mode 100644 src/main/webapp/app/entities/preferences/delete/preferences-delete-dialog.component.spec.ts create mode 100644 src/main/webapp/app/entities/preferences/delete/preferences-delete-dialog.component.ts create mode 100644 src/main/webapp/app/entities/preferences/detail/preferences-detail.component.html create mode 100644 src/main/webapp/app/entities/preferences/detail/preferences-detail.component.spec.ts create mode 100644 src/main/webapp/app/entities/preferences/detail/preferences-detail.component.ts create mode 100644 src/main/webapp/app/entities/preferences/list/preferences.component.html create mode 100644 src/main/webapp/app/entities/preferences/list/preferences.component.spec.ts create mode 100644 src/main/webapp/app/entities/preferences/list/preferences.component.ts create mode 100644 src/main/webapp/app/entities/preferences/preferences.model.ts create mode 100644 src/main/webapp/app/entities/preferences/preferences.module.ts create mode 100644 src/main/webapp/app/entities/preferences/preferences.test-samples.ts create mode 100644 src/main/webapp/app/entities/preferences/route/preferences-routing-resolve.service.spec.ts create mode 100644 src/main/webapp/app/entities/preferences/route/preferences-routing-resolve.service.ts create mode 100644 src/main/webapp/app/entities/preferences/route/preferences-routing.module.ts create mode 100644 src/main/webapp/app/entities/preferences/service/preferences.service.spec.ts create mode 100644 src/main/webapp/app/entities/preferences/service/preferences.service.ts create mode 100644 src/main/webapp/app/entities/preferences/update/preferences-form.service.spec.ts create mode 100644 src/main/webapp/app/entities/preferences/update/preferences-form.service.ts create mode 100644 src/main/webapp/app/entities/preferences/update/preferences-update.component.html create mode 100644 src/main/webapp/app/entities/preferences/update/preferences-update.component.spec.ts create mode 100644 src/main/webapp/app/entities/preferences/update/preferences-update.component.ts create mode 100644 src/main/webapp/app/entities/user/user.model.ts create mode 100644 src/main/webapp/app/entities/user/user.service.spec.ts create mode 100644 src/main/webapp/app/entities/user/user.service.ts create mode 100644 src/main/webapp/app/entities/weight/delete/weight-delete-dialog.component.html create mode 100644 src/main/webapp/app/entities/weight/delete/weight-delete-dialog.component.spec.ts create mode 100644 src/main/webapp/app/entities/weight/delete/weight-delete-dialog.component.ts create mode 100644 src/main/webapp/app/entities/weight/detail/weight-detail.component.html create mode 100644 src/main/webapp/app/entities/weight/detail/weight-detail.component.spec.ts create mode 100644 src/main/webapp/app/entities/weight/detail/weight-detail.component.ts create mode 100644 src/main/webapp/app/entities/weight/list/weight.component.html create mode 100644 src/main/webapp/app/entities/weight/list/weight.component.spec.ts create mode 100644 src/main/webapp/app/entities/weight/list/weight.component.ts create mode 100644 src/main/webapp/app/entities/weight/route/weight-routing-resolve.service.spec.ts create mode 100644 src/main/webapp/app/entities/weight/route/weight-routing-resolve.service.ts create mode 100644 src/main/webapp/app/entities/weight/route/weight-routing.module.ts create mode 100644 src/main/webapp/app/entities/weight/service/weight.service.spec.ts create mode 100644 src/main/webapp/app/entities/weight/service/weight.service.ts create mode 100644 src/main/webapp/app/entities/weight/update/weight-form.service.spec.ts create mode 100644 src/main/webapp/app/entities/weight/update/weight-form.service.ts create mode 100644 src/main/webapp/app/entities/weight/update/weight-update.component.html create mode 100644 src/main/webapp/app/entities/weight/update/weight-update.component.spec.ts create mode 100644 src/main/webapp/app/entities/weight/update/weight-update.component.ts create mode 100644 src/main/webapp/app/entities/weight/weight.model.ts create mode 100644 src/main/webapp/app/entities/weight/weight.module.ts create mode 100644 src/main/webapp/app/entities/weight/weight.test-samples.ts create mode 100644 src/main/webapp/app/home/home.component.html create mode 100644 src/main/webapp/app/home/home.component.scss create mode 100644 src/main/webapp/app/home/home.component.spec.ts create mode 100644 src/main/webapp/app/home/home.component.ts create mode 100644 src/main/webapp/app/home/home.module.ts create mode 100644 src/main/webapp/app/home/home.route.ts create mode 100644 src/main/webapp/app/layouts/error/error.component.html create mode 100644 src/main/webapp/app/layouts/error/error.component.ts create mode 100644 src/main/webapp/app/layouts/error/error.route.ts create mode 100644 src/main/webapp/app/layouts/footer/footer.component.html create mode 100644 src/main/webapp/app/layouts/footer/footer.component.ts create mode 100644 src/main/webapp/app/layouts/main/main.component.html create mode 100644 src/main/webapp/app/layouts/main/main.component.spec.ts create mode 100644 src/main/webapp/app/layouts/main/main.component.ts create mode 100644 src/main/webapp/app/layouts/navbar/active-menu.directive.ts create mode 100644 src/main/webapp/app/layouts/navbar/navbar.component.html create mode 100644 src/main/webapp/app/layouts/navbar/navbar.component.scss create mode 100644 src/main/webapp/app/layouts/navbar/navbar.component.spec.ts create mode 100644 src/main/webapp/app/layouts/navbar/navbar.component.ts create mode 100644 src/main/webapp/app/layouts/navbar/navbar.route.ts create mode 100644 src/main/webapp/app/layouts/profiles/page-ribbon.component.scss create mode 100644 src/main/webapp/app/layouts/profiles/page-ribbon.component.spec.ts create mode 100644 src/main/webapp/app/layouts/profiles/page-ribbon.component.ts create mode 100644 src/main/webapp/app/layouts/profiles/profile-info.model.ts create mode 100644 src/main/webapp/app/layouts/profiles/profile.service.ts create mode 100644 src/main/webapp/app/login/login.component.html create mode 100644 src/main/webapp/app/login/login.component.spec.ts create mode 100644 src/main/webapp/app/login/login.component.ts create mode 100644 src/main/webapp/app/login/login.model.ts create mode 100644 src/main/webapp/app/login/login.module.ts create mode 100644 src/main/webapp/app/login/login.route.ts create mode 100644 src/main/webapp/app/login/login.service.ts create mode 100644 src/main/webapp/app/shared/alert/alert-error.component.html create mode 100644 src/main/webapp/app/shared/alert/alert-error.component.spec.ts create mode 100644 src/main/webapp/app/shared/alert/alert-error.component.ts create mode 100644 src/main/webapp/app/shared/alert/alert-error.model.ts create mode 100644 src/main/webapp/app/shared/alert/alert.component.html create mode 100644 src/main/webapp/app/shared/alert/alert.component.spec.ts create mode 100644 src/main/webapp/app/shared/alert/alert.component.ts create mode 100644 src/main/webapp/app/shared/auth/has-any-authority.directive.spec.ts create mode 100644 src/main/webapp/app/shared/auth/has-any-authority.directive.ts create mode 100644 src/main/webapp/app/shared/date/duration.pipe.ts create mode 100644 src/main/webapp/app/shared/date/format-medium-date.pipe.spec.ts create mode 100644 src/main/webapp/app/shared/date/format-medium-date.pipe.ts create mode 100644 src/main/webapp/app/shared/date/format-medium-datetime.pipe.spec.ts create mode 100644 src/main/webapp/app/shared/date/format-medium-datetime.pipe.ts create mode 100644 src/main/webapp/app/shared/filter/filter.component.html create mode 100644 src/main/webapp/app/shared/filter/filter.component.ts create mode 100644 src/main/webapp/app/shared/filter/filter.model.spec.ts create mode 100644 src/main/webapp/app/shared/filter/filter.model.ts create mode 100644 src/main/webapp/app/shared/language/find-language-from-key.pipe.ts create mode 100644 src/main/webapp/app/shared/language/translate.directive.spec.ts create mode 100644 src/main/webapp/app/shared/language/translate.directive.ts create mode 100644 src/main/webapp/app/shared/language/translation.module.ts create mode 100644 src/main/webapp/app/shared/pagination/item-count.component.spec.ts create mode 100644 src/main/webapp/app/shared/pagination/item-count.component.ts create mode 100644 src/main/webapp/app/shared/shared-libs.module.ts create mode 100644 src/main/webapp/app/shared/shared.module.ts create mode 100644 src/main/webapp/app/shared/sort/sort-by.directive.spec.ts create mode 100644 src/main/webapp/app/shared/sort/sort-by.directive.ts create mode 100644 src/main/webapp/app/shared/sort/sort.directive.spec.ts create mode 100644 src/main/webapp/app/shared/sort/sort.directive.ts create mode 100644 src/main/webapp/app/shared/sort/sort.service.ts create mode 100644 src/main/webapp/bootstrap.ts create mode 100644 src/main/webapp/content/css/loading.css create mode 100644 src/main/webapp/content/images/jhipster_family_member_0.svg create mode 100644 src/main/webapp/content/images/jhipster_family_member_0_head-192.png create mode 100644 src/main/webapp/content/images/jhipster_family_member_0_head-256.png create mode 100644 src/main/webapp/content/images/jhipster_family_member_0_head-384.png create mode 100644 src/main/webapp/content/images/jhipster_family_member_0_head-512.png create mode 100644 src/main/webapp/content/images/jhipster_family_member_1.svg create mode 100644 src/main/webapp/content/images/jhipster_family_member_1_head-192.png create mode 100644 src/main/webapp/content/images/jhipster_family_member_1_head-256.png create mode 100644 src/main/webapp/content/images/jhipster_family_member_1_head-384.png create mode 100644 src/main/webapp/content/images/jhipster_family_member_1_head-512.png create mode 100644 src/main/webapp/content/images/jhipster_family_member_2.svg create mode 100644 src/main/webapp/content/images/jhipster_family_member_2_head-192.png create mode 100644 src/main/webapp/content/images/jhipster_family_member_2_head-256.png create mode 100644 src/main/webapp/content/images/jhipster_family_member_2_head-384.png create mode 100644 src/main/webapp/content/images/jhipster_family_member_2_head-512.png create mode 100644 src/main/webapp/content/images/jhipster_family_member_3.svg create mode 100644 src/main/webapp/content/images/jhipster_family_member_3_head-192.png create mode 100644 src/main/webapp/content/images/jhipster_family_member_3_head-256.png create mode 100644 src/main/webapp/content/images/jhipster_family_member_3_head-384.png create mode 100644 src/main/webapp/content/images/jhipster_family_member_3_head-512.png create mode 100644 src/main/webapp/content/images/logo-jhipster.png create mode 100644 src/main/webapp/content/scss/_bootstrap-variables.scss create mode 100644 src/main/webapp/content/scss/global.scss create mode 100644 src/main/webapp/content/scss/vendor.scss create mode 100644 src/main/webapp/declarations.d.ts create mode 100644 src/main/webapp/favicon.ico create mode 100644 src/main/webapp/i18n/en/activate.json create mode 100644 src/main/webapp/i18n/en/bloodPressure.json create mode 100644 src/main/webapp/i18n/en/configuration.json create mode 100644 src/main/webapp/i18n/en/elasticsearch-reindex.json create mode 100644 src/main/webapp/i18n/en/error.json create mode 100644 src/main/webapp/i18n/en/global.json create mode 100644 src/main/webapp/i18n/en/health.json create mode 100644 src/main/webapp/i18n/en/home.json create mode 100644 src/main/webapp/i18n/en/login.json create mode 100644 src/main/webapp/i18n/en/logs.json create mode 100644 src/main/webapp/i18n/en/metrics.json create mode 100644 src/main/webapp/i18n/en/password.json create mode 100644 src/main/webapp/i18n/en/points.json create mode 100644 src/main/webapp/i18n/en/preferences.json create mode 100644 src/main/webapp/i18n/en/register.json create mode 100644 src/main/webapp/i18n/en/reset.json create mode 100644 src/main/webapp/i18n/en/sessions.json create mode 100644 src/main/webapp/i18n/en/settings.json create mode 100644 src/main/webapp/i18n/en/units.json create mode 100644 src/main/webapp/i18n/en/user-management.json create mode 100644 src/main/webapp/i18n/en/weight.json create mode 100644 src/main/webapp/i18n/fr/activate.json create mode 100644 src/main/webapp/i18n/fr/bloodPressure.json create mode 100644 src/main/webapp/i18n/fr/configuration.json create mode 100644 src/main/webapp/i18n/fr/elasticsearch-reindex.json create mode 100644 src/main/webapp/i18n/fr/error.json create mode 100644 src/main/webapp/i18n/fr/global.json create mode 100644 src/main/webapp/i18n/fr/health.json create mode 100644 src/main/webapp/i18n/fr/home.json create mode 100644 src/main/webapp/i18n/fr/login.json create mode 100644 src/main/webapp/i18n/fr/logs.json create mode 100644 src/main/webapp/i18n/fr/metrics.json create mode 100644 src/main/webapp/i18n/fr/password.json create mode 100644 src/main/webapp/i18n/fr/points.json create mode 100644 src/main/webapp/i18n/fr/preferences.json create mode 100644 src/main/webapp/i18n/fr/register.json create mode 100644 src/main/webapp/i18n/fr/reset.json create mode 100644 src/main/webapp/i18n/fr/sessions.json create mode 100644 src/main/webapp/i18n/fr/settings.json create mode 100644 src/main/webapp/i18n/fr/units.json create mode 100644 src/main/webapp/i18n/fr/user-management.json create mode 100644 src/main/webapp/i18n/fr/weight.json create mode 100644 src/main/webapp/index.html create mode 100644 src/main/webapp/main.ts create mode 100644 src/main/webapp/manifest.webapp create mode 100644 src/main/webapp/polyfills.ts create mode 100644 src/main/webapp/robots.txt create mode 100644 src/main/webapp/swagger-ui/dist/images/throbber.gif create mode 100644 src/main/webapp/swagger-ui/index.html create mode 100644 src/test/java/org/jhipster/health/IntegrationTest.java create mode 100644 src/test/java/org/jhipster/health/TechnicalStructureTest.java create mode 100644 src/test/java/org/jhipster/health/config/AsyncSyncConfiguration.java create mode 100644 src/test/java/org/jhipster/health/config/ElasticsearchTestConfiguration.java create mode 100644 src/test/java/org/jhipster/health/config/ElasticsearchTestContainer.java create mode 100644 src/test/java/org/jhipster/health/config/EmbeddedElasticsearch.java create mode 100644 src/test/java/org/jhipster/health/config/EmbeddedSQL.java create mode 100644 src/test/java/org/jhipster/health/config/PostgreSqlTestContainer.java create mode 100644 src/test/java/org/jhipster/health/config/SpringBootTestClassOrderer.java create mode 100644 src/test/java/org/jhipster/health/config/SqlTestContainer.java create mode 100644 src/test/java/org/jhipster/health/config/StaticResourcesWebConfigurerTest.java create mode 100644 src/test/java/org/jhipster/health/config/TestContainersSpringContextCustomizerFactory.java create mode 100644 src/test/java/org/jhipster/health/config/WebConfigurerTest.java create mode 100644 src/test/java/org/jhipster/health/config/WebConfigurerTestController.java create mode 100644 src/test/java/org/jhipster/health/config/timezone/HibernateTimeZoneIT.java create mode 100644 src/test/java/org/jhipster/health/domain/BloodPressureTest.java create mode 100644 src/test/java/org/jhipster/health/domain/PointsTest.java create mode 100644 src/test/java/org/jhipster/health/domain/PreferencesTest.java create mode 100644 src/test/java/org/jhipster/health/domain/WeightTest.java create mode 100644 src/test/java/org/jhipster/health/management/SecurityMetersServiceTests.java create mode 100644 src/test/java/org/jhipster/health/repository/timezone/DateTimeWrapper.java create mode 100644 src/test/java/org/jhipster/health/repository/timezone/DateTimeWrapperRepository.java create mode 100644 src/test/java/org/jhipster/health/security/DomainUserDetailsServiceIT.java create mode 100644 src/test/java/org/jhipster/health/security/SecurityUtilsUnitTest.java create mode 100644 src/test/java/org/jhipster/health/security/jwt/JWTFilterTest.java create mode 100644 src/test/java/org/jhipster/health/security/jwt/TokenProviderSecurityMetersTests.java create mode 100644 src/test/java/org/jhipster/health/security/jwt/TokenProviderTest.java create mode 100644 src/test/java/org/jhipster/health/service/MailServiceIT.java create mode 100644 src/test/java/org/jhipster/health/service/UserServiceIT.java create mode 100644 src/test/java/org/jhipster/health/service/mapper/UserMapperTest.java create mode 100644 src/test/java/org/jhipster/health/web/rest/AccountResourceIT.java create mode 100644 src/test/java/org/jhipster/health/web/rest/BloodPressureResourceIT.java create mode 100644 src/test/java/org/jhipster/health/web/rest/ClientForwardControllerTest.java create mode 100644 src/test/java/org/jhipster/health/web/rest/PointsResourceIT.java create mode 100644 src/test/java/org/jhipster/health/web/rest/PreferencesResourceIT.java create mode 100644 src/test/java/org/jhipster/health/web/rest/PublicUserResourceIT.java create mode 100644 src/test/java/org/jhipster/health/web/rest/TestUtil.java create mode 100644 src/test/java/org/jhipster/health/web/rest/UserJWTControllerIT.java create mode 100644 src/test/java/org/jhipster/health/web/rest/UserResourceIT.java create mode 100644 src/test/java/org/jhipster/health/web/rest/WeightResourceIT.java create mode 100644 src/test/java/org/jhipster/health/web/rest/WithUnauthenticatedMockUser.java create mode 100644 src/test/java/org/jhipster/health/web/rest/errors/ExceptionTranslatorIT.java create mode 100644 src/test/java/org/jhipster/health/web/rest/errors/ExceptionTranslatorTestController.java create mode 100644 src/test/javascript/cypress/.eslintrc.json create mode 100644 src/test/javascript/cypress/e2e/account/login-page.cy.ts create mode 100644 src/test/javascript/cypress/e2e/account/password-page.cy.ts create mode 100644 src/test/javascript/cypress/e2e/account/register-page.cy.ts create mode 100644 src/test/javascript/cypress/e2e/account/reset-password-page.cy.ts create mode 100644 src/test/javascript/cypress/e2e/account/settings-page.cy.ts create mode 100644 src/test/javascript/cypress/e2e/administration/administration.cy.ts create mode 100644 src/test/javascript/cypress/e2e/entity/blood-pressure.cy.ts create mode 100644 src/test/javascript/cypress/e2e/entity/points.cy.ts create mode 100644 src/test/javascript/cypress/e2e/entity/preferences.cy.ts create mode 100644 src/test/javascript/cypress/e2e/entity/weight.cy.ts create mode 100644 src/test/javascript/cypress/fixtures/integration-test.png create mode 100644 src/test/javascript/cypress/plugins/index.ts create mode 100644 src/test/javascript/cypress/support/commands.ts create mode 100644 src/test/javascript/cypress/support/entity.ts create mode 100644 src/test/javascript/cypress/support/index.ts create mode 100644 src/test/javascript/cypress/support/management.ts create mode 100644 src/test/javascript/cypress/support/navbar.ts create mode 100644 src/test/javascript/cypress/tsconfig.json create mode 100644 src/test/resources/META-INF/spring.factories create mode 100644 src/test/resources/config/application-testdev.yml create mode 100644 src/test/resources/config/application-testprod.yml create mode 100644 src/test/resources/config/application.yml create mode 100644 src/test/resources/config/bootstrap.yml create mode 100644 src/test/resources/i18n/messages_en.properties create mode 100644 src/test/resources/i18n/messages_fr.properties create mode 100644 src/test/resources/junit-platform.properties create mode 100644 src/test/resources/logback.xml create mode 100644 src/test/resources/templates/mail/activationEmail.html create mode 100644 src/test/resources/templates/mail/creationEmail.html create mode 100644 src/test/resources/templates/mail/passwordResetEmail.html create mode 100644 src/test/resources/templates/mail/testEmail.html create mode 100644 src/test/resources/testcontainers.properties create mode 100644 tsconfig.app.json create mode 100644 tsconfig.json create mode 100644 tsconfig.spec.json create mode 100644 webpack/environment.js create mode 100644 webpack/logo-jhipster.png create mode 100644 webpack/proxy.conf.js create mode 100644 webpack/webpack.custom.js diff --git a/.browserslistrc b/.browserslistrc new file mode 100644 index 00000000..0ccadaf3 --- /dev/null +++ b/.browserslistrc @@ -0,0 +1,18 @@ +# This file is used by the build system to adjust CSS and JS output to support the specified browsers below. +# For additional information regarding the format and rule options, please see: +# https://github.com/browserslist/browserslist#queries + +# For the full list of supported browsers by the Angular framework, please see: +# https://angular.io/guide/browser-support + +# You can see what browsers were selected by your queries by running: +# npx browserslist + +last 1 Chrome version +last 1 Firefox version +last 2 Edge major versions +last 2 Safari major versions +last 2 iOS major versions +Firefox ESR +not IE 9-10 # Angular support for IE 9-10 has been deprecated and will be removed as of Angular v11. To opt-in, remove the 'not' prefix on this line. +not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line. diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 00000000..98a7ee76 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,25 @@ +# See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.209.6/containers/java/.devcontainer/base.Dockerfile + +# [Choice] Java version (use -bullseye variants on local arm64/Apple Silicon): 11, 17, 11-bullseye, 17-bullseye, 11-buster, 17-buster +ARG VARIANT="11" +FROM mcr.microsoft.com/vscode/devcontainers/java:0-${VARIANT} + +# [Option] Install Maven +ARG INSTALL_MAVEN="false" +ARG MAVEN_VERSION="" +# [Option] Install Gradle +ARG INSTALL_GRADLE="false" +ARG GRADLE_VERSION="" +RUN if [ "${INSTALL_MAVEN}" = "true" ]; then su vscode -c "umask 0002 && . /usr/local/sdkman/bin/sdkman-init.sh && sdk install maven \"${MAVEN_VERSION}\""; fi \ + && if [ "${INSTALL_GRADLE}" = "true" ]; then su vscode -c "umask 0002 && . /usr/local/sdkman/bin/sdkman-init.sh && sdk install gradle \"${GRADLE_VERSION}\""; fi + +# [Choice] Node.js version: none, lts/*, 16, 14, 12, 10 +ARG NODE_VERSION="none" +RUN if [ "${NODE_VERSION}" != "none" ]; then su vscode -c "umask 0002 && . /usr/local/share/nvm/nvm.sh && nvm install ${NODE_VERSION} 2>&1"; fi + +# [Optional] Uncomment this section to install additional OS packages. +# RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ +# && apt-get -y install --no-install-recommends + +# [Optional] Uncomment this line to install global node packages. +# RUN su vscode -c "source /usr/local/share/nvm/nvm.sh && npm install -g " 2>&1 diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000..ee261a6c --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,48 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: +// https://github.com/microsoft/vscode-dev-containers/tree/v0.209.6/containers/java +{ + "name": "TwentyOnePoints", + "build": { + "dockerfile": "Dockerfile", + "args": { + // Update the VARIANT arg to pick a Java version: 11, 17 + // Append -bullseye or -buster to pin to an OS version. + // Use the -bullseye variants on local arm64/Apple Silicon. + "VARIANT": "11", + // Options + "INSTALL_MAVEN": "false", + "INSTALL_GRADLE": "true", + "NODE_VERSION": "lts/*" + } + }, + + // Set *default* container specific settings.json values on container create. + "settings": { + "java.home": "/docker-java-home" + }, + + // Add the IDs of extensions you want installed when the container is created. + "extensions": [ + "angular.ng-template", + "christian-kohler.npm-intellisense", + "firsttris.vscode-jest-runner", + "ms-vscode.vscode-typescript-tslint-plugin", + "dbaeumer.vscode-eslint", + "vscjava.vscode-java-pack", + "pivotal.vscode-boot-dev-pack", + "esbenp.prettier-vscode" + ], + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + "forwardPorts": [4200, 3001, 9000, 8080], + + // Use 'postCreateCommand' to run commands after the container is created. + // "postCreateCommand": "java -version", + + // Comment out connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. + "remoteUser": "vscode", + "features": { + "docker-in-docker": "latest", + "docker-from-docker": "latest" + } +} diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..c2fa6a2d --- /dev/null +++ b/.editorconfig @@ -0,0 +1,23 @@ +# EditorConfig helps developers define and maintain consistent +# coding styles between different editors and IDEs +# editorconfig.org + +root = true + +[*] + +# We recommend you to keep these unchanged +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +# Change these settings to your own preference +indent_style = space +indent_size = 4 + +[*.{ts,tsx,js,jsx,json,css,scss,yml,html,vue}] +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 00000000..0f4bda1a --- /dev/null +++ b/.eslintignore @@ -0,0 +1,8 @@ +node_modules/ +src/main/docker/ +jest.conf.js +webpack/ +target/ +build/ +node/ +postcss.config.js diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 00000000..d5813308 --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,99 @@ +{ + "parser": "@typescript-eslint/parser", + "plugins": ["@angular-eslint/eslint-plugin", "@typescript-eslint"], + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:@typescript-eslint/recommended-requiring-type-checking", + "plugin:@angular-eslint/recommended", + "prettier", + "eslint-config-prettier" + ], + "env": { + "browser": true, + "es6": true, + "commonjs": true + }, + "parserOptions": { + "ecmaVersion": 2018, + "sourceType": "module", + "project": ["./tsconfig.app.json", "./src/test/javascript/cypress/tsconfig.json", "./tsconfig.spec.json"] + }, + "rules": { + "@angular-eslint/component-selector": [ + "error", + { + "type": "element", + "prefix": "jhi", + "style": "kebab-case" + } + ], + "@angular-eslint/directive-selector": [ + "error", + { + "type": "attribute", + "prefix": "jhi", + "style": "camelCase" + } + ], + "@angular-eslint/relative-url-prefix": "error", + "@typescript-eslint/ban-types": [ + "error", + { + "extendDefaults": true, + "types": { + "{}": false + } + } + ], + "@typescript-eslint/explicit-function-return-type": ["error", { "allowExpressions": true }], + "@typescript-eslint/explicit-module-boundary-types": "off", + "@typescript-eslint/member-ordering": [ + "error", + { + "default": [ + "public-static-field", + "protected-static-field", + "private-static-field", + "public-instance-field", + "protected-instance-field", + "private-instance-field", + "constructor", + "public-static-method", + "protected-static-method", + "private-static-method", + "public-instance-method", + "protected-instance-method", + "private-instance-method" + ] + } + ], + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-floating-promises": "off", + "@typescript-eslint/no-non-null-assertion": "off", + "@typescript-eslint/no-parameter-properties": ["warn", { "allows": ["public", "private", "protected"] }], + "@typescript-eslint/no-shadow": ["error"], + "@typescript-eslint/no-unnecessary-condition": "error", + "@typescript-eslint/no-unsafe-argument": "off", + "@typescript-eslint/no-unsafe-assignment": "off", + "@typescript-eslint/no-unsafe-call": "off", + "@typescript-eslint/no-unsafe-member-access": "off", + "@typescript-eslint/prefer-nullish-coalescing": "error", + "@typescript-eslint/prefer-optional-chain": "error", + "@typescript-eslint/unbound-method": "off", + "arrow-body-style": "error", + "curly": "error", + "eqeqeq": ["error", "always", { "null": "ignore" }], + "guard-for-in": "error", + "no-bitwise": "error", + "no-caller": "error", + "no-console": ["error", { "allow": ["warn", "error"] }], + "no-eval": "error", + "no-labels": "error", + "no-new": "error", + "no-new-wrappers": "error", + "object-shorthand": ["error", "always", { "avoidExplicitReturnArrows": true }], + "radix": "error", + "spaced-comment": ["warn", "always"] + } +} diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..ca61722d --- /dev/null +++ b/.gitattributes @@ -0,0 +1,150 @@ +# This file is inspired by https://github.com/alexkaratarakis/gitattributes +# +# Auto detect text files and perform LF normalization +# http://davidlaing.com/2012/09/19/customise-your-gitattributes-to-become-a-git-ninja/ +* text=auto + +# The above will handle all files NOT found below +# These files are text and should be normalized (Convert crlf => lf) + +*.bat text eol=crlf +*.cmd text eol=crlf +*.ps1 text eol=crlf +*.coffee text +*.css text +*.cql text +*.df text +*.ejs text +*.html text +*.java text +*.js text +*.json text +*.less text +*.properties text +*.sass text +*.scss text +*.sh text eol=lf +*.sql text +*.txt text +*.ts text +*.xml text +*.yaml text +*.yml text + +# Documents +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain +*.markdown text +*.md text +*.adoc text +*.textile text +*.mustache text +*.csv text +*.tab text +*.tsv text +*.txt text +AUTHORS text +CHANGELOG text +CHANGES text +CONTRIBUTING text +COPYING text +copyright text +*COPYRIGHT* text +INSTALL text +license text +LICENSE text +NEWS text +readme text +*README* text +TODO text + +# Graphics +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary +*.tif binary +*.tiff binary +*.ico binary +# SVG treated as an asset (binary) by default. If you want to treat it as text, +# comment-out the following line and uncomment the line after. +*.svg binary +#*.svg text +*.eps binary + +# These files are binary and should be left untouched +# (binary is a macro for -text -diff) +*.class binary +*.jar binary +*.war binary + +## LINTERS +.csslintrc text +.eslintrc text +.jscsrc text +.jshintrc text +.jshintignore text +.stylelintrc text + +## CONFIGS +*.conf text +*.config text +.editorconfig text +.gitattributes text +.gitconfig text +.gitignore text +.htaccess text +*.npmignore text + +## HEROKU +Procfile text +.slugignore text + +## AUDIO +*.kar binary +*.m4a binary +*.mid binary +*.midi binary +*.mp3 binary +*.ogg binary +*.ra binary + +## VIDEO +*.3gpp binary +*.3gp binary +*.as binary +*.asf binary +*.asx binary +*.fla binary +*.flv binary +*.m4v binary +*.mng binary +*.mov binary +*.mp4 binary +*.mpeg binary +*.mpg binary +*.swc binary +*.swf binary +*.webm binary + +## ARCHIVES +*.7z binary +*.gz binary +*.rar binary +*.tar binary +*.zip binary + +## FONTS +*.ttf binary +*.eot binary +*.otf binary +*.woff binary +*.woff2 binary diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..4887de74 --- /dev/null +++ b/.gitignore @@ -0,0 +1,159 @@ +###################### +# Project Specific +###################### +/src/main/webapp/content/css/main.css +/build/resources/main/static/** +/src/test/javascript/coverage/ + +###################### +# Node +###################### +/node/ +node_tmp/ +node_modules/ +npm-debug.log.* +/.awcache/* +/.cache-loader/* + +###################### +# SASS +###################### +.sass-cache/ + +###################### +# Eclipse +###################### +*.pydevproject +.project +.metadata +tmp/ +tmp/**/* +*.tmp +*.bak +*.swp +*~.nib +local.properties +.classpath +.settings/ +.loadpath +.factorypath +/src/main/resources/rebel.xml + +# External tool builders +.externalToolBuilders/** + +# Locally stored "Eclipse launch configurations" +*.launch + +# CDT-specific +.cproject + +# PDT-specific +.buildpath + +# STS-specific +/.sts4-cache/* + +###################### +# IntelliJ +###################### +.idea/ +*.iml +*.iws +*.ipr +*.ids +*.orig +classes/ +out/ + +###################### +# Visual Studio Code +###################### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +###################### +# Maven +###################### +/log/ +/target/ + +###################### +# Gradle +###################### +.gradle/ +/build/ + +###################### +# Package Files +###################### +*.jar +*.war +*.ear +*.db + +###################### +# Windows +###################### +# Windows image file caches +Thumbs.db + +# Folder config file +Desktop.ini + +###################### +# Mac OSX +###################### +.DS_Store +.svn + +# Thumbnails +._* + +# Files that might appear on external disk +.Spotlight-V100 +.Trashes + +###################### +# Directories +###################### +/bin/ +/deploy/ + +###################### +# Logs +###################### +*.log* + +###################### +# Others +###################### +*.class +*.*~ +*~ +.merge_file* + +###################### +# Gradle Wrapper +###################### +!gradle/wrapper/gradle-wrapper.jar + +###################### +# Maven Wrapper +###################### +!.mvn/wrapper/maven-wrapper.jar + +###################### +# ESLint +###################### +.eslintcache + +###################### +# Code coverage +###################### +/coverage/ +/.nyc_output/ diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 00000000..adefefb3 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,5 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + + +"$(dirname "$0")/../npmw" exec --no-install lint-staged diff --git a/.jhipster/BloodPressure.json b/.jhipster/BloodPressure.json new file mode 100644 index 00000000..71786e34 --- /dev/null +++ b/.jhipster/BloodPressure.json @@ -0,0 +1,34 @@ +{ + "changelogDate": "20221108001642", + "fields": [ + { + "fieldName": "timestamp", + "fieldType": "ZonedDateTime", + "fieldValidateRules": ["required"] + }, + { + "fieldName": "systolic", + "fieldType": "Integer", + "fieldValidateRules": ["required"] + }, + { + "fieldName": "diastolic", + "fieldType": "Integer", + "fieldValidateRules": ["required"] + } + ], + "name": "BloodPressure", + "pagination": "infinite-scroll", + "readOnly": false, + "relationships": [ + { + "otherEntityField": "login", + "otherEntityName": "user", + "otherEntityRelationshipName": "bloodPressure", + "ownerSide": true, + "relationshipName": "user", + "relationshipType": "many-to-one" + } + ], + "service": "no" +} diff --git a/.jhipster/Points.json b/.jhipster/Points.json new file mode 100644 index 00000000..54f837e9 --- /dev/null +++ b/.jhipster/Points.json @@ -0,0 +1,42 @@ +{ + "changelogDate": "20221108000520", + "fields": [ + { + "fieldName": "date", + "fieldType": "LocalDate", + "fieldValidateRules": ["required"] + }, + { + "fieldName": "exercise", + "fieldType": "Integer" + }, + { + "fieldName": "meals", + "fieldType": "Integer" + }, + { + "fieldName": "alcohol", + "fieldType": "Integer" + }, + { + "fieldName": "notes", + "fieldType": "String", + "fieldValidateRules": ["maxlength"], + "fieldValidateRulesMaxlength": "140" + } + ], + "name": "Points", + "pagination": "pagination", + "readOnly": false, + "relationships": [ + { + "otherEntityField": "login", + "otherEntityName": "user", + "otherEntityRelationshipName": "points", + "ownerSide": true, + "relationshipName": "user", + "relationshipType": "many-to-one" + } + ], + "service": "no" +} diff --git a/.jhipster/Preferences.json b/.jhipster/Preferences.json new file mode 100644 index 00000000..3f28a492 --- /dev/null +++ b/.jhipster/Preferences.json @@ -0,0 +1,33 @@ +{ + "changelogDate": "20221108002021", + "fields": [ + { + "fieldName": "weeklyGoal", + "fieldType": "Integer", + "fieldValidateRules": ["required", "min", "max"], + "fieldValidateRulesMax": "21", + "fieldValidateRulesMin": "10" + }, + { + "fieldName": "weightUnits", + "fieldType": "Units", + "fieldValidateRules": ["required"], + "fieldValues": "KG,LB" + } + ], + "name": "Preferences", + "pagination": "no", + "readOnly": false, + "relationships": [ + { + "id": false, + "otherEntityField": "login", + "otherEntityName": "user", + "otherEntityRelationshipName": "preferences", + "ownerSide": true, + "relationshipName": "user", + "relationshipType": "one-to-one" + } + ], + "service": "no" +} diff --git a/.jhipster/Weight.json b/.jhipster/Weight.json new file mode 100644 index 00000000..ceb61e2b --- /dev/null +++ b/.jhipster/Weight.json @@ -0,0 +1,29 @@ +{ + "changelogDate": "20221108001452", + "fields": [ + { + "fieldName": "timestamp", + "fieldType": "ZonedDateTime", + "fieldValidateRules": ["required"] + }, + { + "fieldName": "weight", + "fieldType": "Double", + "fieldValidateRules": ["required"] + } + ], + "name": "Weight", + "pagination": "infinite-scroll", + "readOnly": false, + "relationships": [ + { + "otherEntityField": "login", + "otherEntityName": "user", + "otherEntityRelationshipName": "weight", + "ownerSide": true, + "relationshipName": "user", + "relationshipType": "many-to-one" + } + ], + "service": "no" +} diff --git a/.jhipster/modules/jhi-hooks.json b/.jhipster/modules/jhi-hooks.json new file mode 100644 index 00000000..b5dd4cbc --- /dev/null +++ b/.jhipster/modules/jhi-hooks.json @@ -0,0 +1,10 @@ +[ + { + "name": "Es Entity Reindexer generator", + "npmPackageName": "generator-jhipster-es-entity-reindexer", + "description": "Generates code to reindex selected entities with Elasticsearch.", + "hookFor": "entity", + "hookType": "post", + "generatorCallback": "jhipster-es-entity-reindexer:app" + } +] diff --git a/.lintstagedrc.js b/.lintstagedrc.js new file mode 100644 index 00000000..68768783 --- /dev/null +++ b/.lintstagedrc.js @@ -0,0 +1,3 @@ +module.exports = { + '{,src/**/,webpack/}*.{md,json,yml,html,cjs,mjs,js,ts,tsx,css,scss,java}': ['prettier --write'], +}; diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..ab0567f5 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,8 @@ +node_modules +target +build +package-lock.json +.git +.mvn +gradle +.gradle diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..6d25fbaf --- /dev/null +++ b/.prettierrc @@ -0,0 +1,18 @@ +# Prettier configuration + +printWidth: 140 +singleQuote: true +tabWidth: 2 +useTabs: false + +# js and ts rules: +arrowParens: avoid + +# jsx and tsx rules: +bracketSameLine: false + +# java rules: +overrides: + - files: "*.java" + options: + tabWidth: 4 diff --git a/.yo-rc.json b/.yo-rc.json new file mode 100644 index 00000000..b101f78b --- /dev/null +++ b/.yo-rc.json @@ -0,0 +1,55 @@ +{ + "generator-jhipster": { + "applicationType": "monolith", + "authenticationType": "jwt", + "baseName": "TwentyOnePoints", + "blueprints": [], + "buildTool": "gradle", + "cacheProvider": "ehcache", + "clientFramework": "angularX", + "clientPackageManager": "npm", + "clientTheme": "none", + "clientThemeVariant": "", + "creationTimestamp": 1662997089611, + "cypressAudit": false, + "cypressCoverage": false, + "databaseType": "sql", + "devDatabaseType": "h2Disk", + "devServerPort": 4200, + "dtoSuffix": "DTO", + "enableGradleEnterprise": false, + "enableHibernateCache": true, + "enableSwaggerCodegen": false, + "enableTranslation": true, + "entities": ["Points", "Weight", "BloodPressure", "Preferences"], + "entitySuffix": "", + "herokuAppName": "health-by-points", + "herokuDeployType": "git", + "herokuJavaVersion": "11", + "jhiPrefix": "jhi", + "jhipsterVersion": "7.9.3", + "jwtSecretKey": "YjAwZDU2ODI3NzNjZjk0NjVhY2UzNGViZmY0YjY1ZGY2MDI5MTc5Y2FlZWYxNzE5Yjg5ZjMxOGZhZTgwZjA5NTFkN2VmNDM3ZGMyZDNjZTViNDAwYTg4NmNlMmM2ZDkxZTMwMDk5YmUxNWNkZmE0YTNiNDlhMGNhZmM4OTk1NmQ=", + "languages": ["en", "fr"], + "lastLiquibaseTimestamp": 1667866821000, + "messageBroker": false, + "microfrontend": false, + "microfrontends": [], + "nativeLanguage": "en", + "otherModules": [], + "packageName": "org.jhipster.health", + "pages": [], + "prodDatabaseType": "postgresql", + "reactive": false, + "searchEngine": "elasticsearch", + "serverPort": "8080", + "serverSideOptions": ["searchEngine:elasticsearch"], + "serviceDiscoveryType": "no", + "skipCheckLengthOfIdentifier": false, + "skipClient": false, + "skipFakeData": false, + "skipUserManagement": false, + "testFrameworks": ["cypress"], + "websocket": false, + "withAdminUi": true + } +} diff --git a/README.md b/README.md new file mode 100644 index 00000000..1f9439bf --- /dev/null +++ b/README.md @@ -0,0 +1,255 @@ +# TwentyOnePoints + +This application was generated using JHipster 7.9.3, you can find documentation and help at [https://www.jhipster.tech/documentation-archive/v7.9.3](https://www.jhipster.tech/documentation-archive/v7.9.3). + +## Project Structure + +Node is required for generation and recommended for development. `package.json` is always generated for a better development experience with prettier, commit hooks, scripts and so on. + +In the project root, JHipster generates configuration files for tools like git, prettier, eslint, husky, and others that are well known and you can find references in the web. + +`/src/*` structure follows default Java structure. + +- `.yo-rc.json` - Yeoman configuration file + JHipster configuration is stored in this file at `generator-jhipster` key. You may find `generator-jhipster-*` for specific blueprints configuration. +- `.yo-resolve` (optional) - Yeoman conflict resolver + Allows to use a specific action when conflicts are found skipping prompts for files that matches a pattern. Each line should match `[pattern] [action]` with pattern been a [Minimatch](https://github.com/isaacs/minimatch#minimatch) pattern and action been one of skip (default if ommited) or force. Lines starting with `#` are considered comments and are ignored. +- `.jhipster/*.json` - JHipster entity configuration files + +- `npmw` - wrapper to use locally installed npm. + JHipster installs Node and npm locally using the build tool by default. This wrapper makes sure npm is installed locally and uses it avoiding some differences different versions can cause. By using `./npmw` instead of the traditional `npm` you can configure a Node-less environment to develop or test your application. +- `/src/main/docker` - Docker configurations for the application and services that the application depends on + +## Development + +Before you can build this project, you must install and configure the following dependencies on your machine: + +1. [Node.js][]: We use Node to run a development web server and build the project. + Depending on your system, you can install Node either from source or as a pre-packaged bundle. + +After installing Node, you should be able to run the following command to install development tools. +You will only need to run this command when dependencies change in [package.json](package.json). + +``` +npm install +``` + +We use npm scripts and [Angular CLI][] with [Webpack][] as our build system. + +Run the following commands in two separate terminals to create a blissful development experience where your browser +auto-refreshes when files change on your hard drive. + +``` +./gradlew -x webapp +npm start +``` + +Npm is also used to manage CSS and JavaScript dependencies used in this application. You can upgrade dependencies by +specifying a newer version in [package.json](package.json). You can also run `npm update` and `npm install` to manage dependencies. +Add the `help` flag on any command to see how you can use it. For example, `npm help update`. + +The `npm run` command will list all of the scripts available to run for this project. + +### PWA Support + +JHipster ships with PWA (Progressive Web App) support, and it's turned off by default. One of the main components of a PWA is a service worker. + +The service worker initialization code is disabled by default. To enable it, uncomment the following code in `src/main/webapp/app/app.module.ts`: + +```typescript +ServiceWorkerModule.register('ngsw-worker.js', { enabled: false }), +``` + +### Managing dependencies + +For example, to add [Leaflet][] library as a runtime dependency of your application, you would run following command: + +``` +npm install --save --save-exact leaflet +``` + +To benefit from TypeScript type definitions from [DefinitelyTyped][] repository in development, you would run following command: + +``` +npm install --save-dev --save-exact @types/leaflet +``` + +Then you would import the JS and CSS files specified in library's installation instructions so that [Webpack][] knows about them: +Edit [src/main/webapp/app/app.module.ts](src/main/webapp/app/app.module.ts) file: + +``` +import 'leaflet/dist/leaflet.js'; +``` + +Edit [src/main/webapp/content/scss/vendor.scss](src/main/webapp/content/scss/vendor.scss) file: + +``` +@import '~leaflet/dist/leaflet.css'; +``` + +Note: There are still a few other things remaining to do for Leaflet that we won't detail here. + +For further instructions on how to develop with JHipster, have a look at [Using JHipster in development][]. + +### Using Angular CLI + +You can also use [Angular CLI][] to generate some custom client code. + +For example, the following command: + +``` +ng generate component my-component +``` + +will generate few files: + +``` +create src/main/webapp/app/my-component/my-component.component.html +create src/main/webapp/app/my-component/my-component.component.ts +update src/main/webapp/app/app.module.ts +``` + +### JHipster Control Center + +JHipster Control Center can help you manage and control your application(s). You can start a local control center server (accessible on http://localhost:7419) with: + +``` +docker-compose -f src/main/docker/jhipster-control-center.yml up +``` + +## Building for production + +### Packaging as jar + +To build the final jar and optimize the TwentyOnePoints application for production, run: + +``` +./gradlew -Pprod clean bootJar +``` + +This will concatenate and minify the client CSS and JavaScript files. It will also modify `index.html` so it references these new files. +To ensure everything worked, run: + +``` +java -jar build/libs/*.jar +``` + +Then navigate to [http://localhost:8080](http://localhost:8080) in your browser. + +Refer to [Using JHipster in production][] for more details. + +### Packaging as war + +To package your application as a war in order to deploy it to an application server, run: + +``` +./gradlew -Pprod -Pwar clean bootWar +``` + +## Testing + +To launch your application's tests, run: + +``` +./gradlew test integrationTest jacocoTestReport +``` + +### Client tests + +Unit tests are run by [Jest][]. They're located in [src/test/javascript/](src/test/javascript/) and can be run with: + +``` +npm test +``` + +UI end-to-end tests are powered by [Cypress][]. They're located in [src/test/javascript/cypress](src/test/javascript/cypress) +and can be run by starting Spring Boot in one terminal (`./gradlew bootRun`) and running the tests (`npm run e2e`) in a second one. + +#### Lighthouse audits + +You can execute automated [lighthouse audits][https://developers.google.com/web/tools/lighthouse/] with [cypress audits][https://github.com/mfrachet/cypress-audit] by running `npm run e2e:cypress:audits`. +You should only run the audits when your application is packaged with the production profile. +The lighthouse report is created in `build/cypress/lhreport.html` + +For more information, refer to the [Running tests page][]. + +### Code quality + +Sonar is used to analyse code quality. You can start a local Sonar server (accessible on http://localhost:9001) with: + +``` +docker-compose -f src/main/docker/sonar.yml up -d +``` + +Note: we have turned off authentication in [src/main/docker/sonar.yml](src/main/docker/sonar.yml) for out of the box experience while trying out SonarQube, for real use cases turn it back on. + +You can run a Sonar analysis with using the [sonar-scanner](https://docs.sonarqube.org/display/SCAN/Analyzing+with+SonarQube+Scanner) or by using the gradle plugin. + +Then, run a Sonar analysis: + +``` +./gradlew -Pprod clean check jacocoTestReport sonarqube +``` + +For more information, refer to the [Code quality page][]. + +## Using Docker to simplify development (optional) + +You can use Docker to improve your JHipster development experience. A number of docker-compose configuration are available in the [src/main/docker](src/main/docker) folder to launch required third party services. + +For example, to start a postgresql database in a docker container, run: + +``` +docker-compose -f src/main/docker/postgresql.yml up -d +``` + +To stop it and remove the container, run: + +``` +docker-compose -f src/main/docker/postgresql.yml down +``` + +You can also fully dockerize your application and all the services that it depends on. +To achieve this, first build a docker image of your app by running: + +``` +npm run java:docker +``` + +Or build a arm64 docker image when using an arm64 processor os like MacOS with M1 processor family running: + +``` +npm run java:docker:arm64 +``` + +Then run: + +``` +docker-compose -f src/main/docker/app.yml up -d +``` + +When running Docker Desktop on MacOS Big Sur or later, consider enabling experimental `Use the new Virtualization framework` for better processing performance ([disk access performance is worse](https://github.com/docker/roadmap/issues/7)). + +For more information refer to [Using Docker and Docker-Compose][], this page also contains information on the docker-compose sub-generator (`jhipster docker-compose`), which is able to generate docker configurations for one or several JHipster applications. + +## Continuous Integration (optional) + +To configure CI for your project, run the ci-cd sub-generator (`jhipster ci-cd`), this will let you generate configuration files for a number of Continuous Integration systems. Consult the [Setting up Continuous Integration][] page for more information. + +[jhipster homepage and latest documentation]: https://www.jhipster.tech +[jhipster 7.9.3 archive]: https://www.jhipster.tech/documentation-archive/v7.9.3 +[using jhipster in development]: https://www.jhipster.tech/documentation-archive/v7.9.3/development/ +[using docker and docker-compose]: https://www.jhipster.tech/documentation-archive/v7.9.3/docker-compose +[using jhipster in production]: https://www.jhipster.tech/documentation-archive/v7.9.3/production/ +[running tests page]: https://www.jhipster.tech/documentation-archive/v7.9.3/running-tests/ +[code quality page]: https://www.jhipster.tech/documentation-archive/v7.9.3/code-quality/ +[setting up continuous integration]: https://www.jhipster.tech/documentation-archive/v7.9.3/setting-up-ci/ +[node.js]: https://nodejs.org/ +[npm]: https://www.npmjs.com/ +[webpack]: https://webpack.github.io/ +[browsersync]: https://www.browsersync.io/ +[jest]: https://facebook.github.io/jest/ +[cypress]: https://www.cypress.io/ +[leaflet]: https://leafletjs.com/ +[definitelytyped]: https://definitelytyped.org/ +[angular cli]: https://cli.angular.io/ diff --git a/angular.json b/angular.json new file mode 100644 index 00000000..639051a2 --- /dev/null +++ b/angular.json @@ -0,0 +1,109 @@ +{ + "$schema": "./node_modules/@angular/cli/lib/config/schema.json", + "version": 1, + "newProjectRoot": "projects", + "projects": { + "twenty-one-points": { + "projectType": "application", + "schematics": { + "@schematics/angular:component": { + "style": "scss" + }, + "@schematics/angular:application": { + "strict": true + } + }, + "root": "", + "sourceRoot": "src/main/webapp", + "prefix": "jhi", + "architect": { + "build": { + "builder": "@angular-builders/custom-webpack:browser", + "options": { + "customWebpackConfig": { + "path": "./webpack/webpack.custom.js" + }, + "outputPath": "build/resources/main/static/", + "index": "src/main/webapp/index.html", + "main": "src/main/webapp/main.ts", + "polyfills": "src/main/webapp/polyfills.ts", + "tsConfig": "tsconfig.app.json", + "inlineStyleLanguage": "scss", + "assets": [ + "src/main/webapp/content", + "src/main/webapp/favicon.ico", + "src/main/webapp/manifest.webapp", + "src/main/webapp/robots.txt" + ], + "styles": ["src/main/webapp/content/scss/vendor.scss", "src/main/webapp/content/scss/global.scss"], + "scripts": [] + }, + "configurations": { + "production": { + "optimization": true, + "outputHashing": "all", + "sourceMap": false, + "namedChunks": false, + "extractLicenses": true, + "vendorChunk": false, + "buildOptimizer": true, + "serviceWorker": true, + "ngswConfigPath": "ngsw-config.json", + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "1mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ] + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "builder": "@angular-builders/custom-webpack:dev-server", + "options": { + "browserTarget": "twenty-one-points:build:development", + "port": 4200 + }, + "configurations": { + "production": { + "browserTarget": "twenty-one-points:build:production" + }, + "development": { + "browserTarget": "twenty-one-points:build:development" + } + }, + "defaultConfiguration": "development" + }, + "test": { + "builder": "@angular-builders/jest:run", + "options": { + "configPath": "jest.conf.js" + } + } + } + } + }, + "cli": { + "cache": { + "enabled": true, + "path": "./build/angular/", + "environment": "all" + }, + "packageManager": "npm" + } +} diff --git a/build.gradle b/build.gradle new file mode 100644 index 00000000..887a89bb --- /dev/null +++ b/build.gradle @@ -0,0 +1,298 @@ +buildscript { + repositories { + gradlePluginPortal() + } + dependencies { + //jhipster-needle-gradle-buildscript-dependency - JHipster will add additional gradle build script plugins here + } +} + +plugins { + id "java" + id "maven-publish" + id "idea" + id "eclipse" + id "jacoco" + id "org.springframework.boot" + id "com.google.cloud.tools.jib" + id "com.gorylenko.gradle-git-properties" + id "com.github.node-gradle.node" + id "org.liquibase.gradle" + id "org.sonarqube" + id "io.spring.nohttp" + id "com.github.andygoossens.gradle-modernizer-plugin" + //jhipster-needle-gradle-plugins - JHipster will add additional gradle plugins here +} + +group = "org.jhipster.health" +version = "0.0.1-SNAPSHOT" + +description = "" + +sourceCompatibility=11 +targetCompatibility=11 +assert System.properties["java.specification.version"] == "11" || "12" || "13" || "14" || "15" || "16" || "17" || "18" + +apply from: "gradle/docker.gradle" +apply from: "gradle/sonar.gradle" +//jhipster-needle-gradle-apply-from - JHipster will add additional gradle scripts to be applied here + +if (project.hasProperty("prod") || project.hasProperty("gae")) { + apply from: "gradle/profile_prod.gradle" +} else { + apply from: "gradle/profile_dev.gradle" +} + +if (project.hasProperty("war")) { + apply from: "gradle/war.gradle" +} + +if (project.hasProperty("gae")) { + apply plugin: 'maven' + apply plugin: 'org.springframework.boot.experimental.thin-launcher' + apply plugin: 'io.spring.dependency-management' + + dependencyManagement { + imports { + mavenBom "tech.jhipster:jhipster-dependencies:${jhipsterDependenciesVersion}" + } + } + appengineStage.dependsOn thinResolve +} + + +idea { + module { + excludeDirs += files("node_modules") + } +} + +eclipse { + sourceSets { + main { + java { + srcDirs += ["build/generated/sources/annotationProcessor/java/main"] + } + } + } +} + +defaultTasks "bootRun" + +springBoot { + mainClass = "org.jhipster.health.TwentyOnePointsApp" +} + +test { + useJUnitPlatform() + exclude "**/*IT*", "**/*IntTest*" + testLogging { + events 'FAILED', 'SKIPPED' + } + jvmArgs += '-Djava.security.egd=file:/dev/./urandom -Xmx512m' + // uncomment if the tests reports are not generated + // see https://github.com/jhipster/generator-jhipster/pull/2771 and https://github.com/jhipster/generator-jhipster/pull/4484 + // ignoreFailures true + reports.html.enabled = false +} + +modernizer { + failOnViolations = true + includeTestClasses = true +} + + + +check.dependsOn integrationTest +task testReport(type: TestReport) { + destinationDir = file("$buildDir/reports/tests") + reportOn test +} + +task integrationTestReport(type: TestReport) { + destinationDir = file("$buildDir/reports/tests") + reportOn integrationTest +} + +if (!project.hasProperty("runList")) { + project.ext.runList = "main" +} + +project.ext.diffChangelogFile = "src/main/resources/config/liquibase/changelog/" + new Date().format("yyyyMMddHHmmss") + "_changelog.xml" + +liquibase { + activities { + main { + driver "org.h2.Driver" + url "jdbc:h2:file:./build/h2db/db/twentyonepoints" + username "TwentyOnePoints" + password "" + changeLogFile "src/main/resources/config/liquibase/master.xml" + defaultSchemaName "" + logLevel "debug" + classpath "src/main/resources/" + } + diffLog { + driver "org.h2.Driver" + url "jdbc:h2:file:./build/h2db/db/twentyonepoints" + username "TwentyOnePoints" + password "" + changeLogFile project.ext.diffChangelogFile + referenceUrl "hibernate:spring:org.jhipster.health.domain?dialect=org.hibernate.dialect.H2Dialect&hibernate.physical_naming_strategy=org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy&hibernate.implicit_naming_strategy=org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy" + defaultSchemaName "" + logLevel "debug" + classpath "$buildDir/classes/java/main" + } + } + + runList = project.ext.runList +} + +gitProperties { + failOnNoGitDirectory = false + keys = ["git.branch", "git.commit.id.abbrev", "git.commit.id.describe"] +} + +checkstyle { + toolVersion "${checkstyleVersion}" + configFile file("checkstyle.xml") + checkstyleTest.enabled = false +} +nohttp { + source.include "build.gradle", "README.md" +} + +configurations { + providedRuntime + implementation.exclude module: "spring-boot-starter-tomcat" + all { + resolutionStrategy { + // Inherited version from Spring Boot can't be used because of regressions: + // To be removed as soon as spring-boot use the same version + force 'org.liquibase:liquibase-core:4.15.0' + } + } +} + +repositories { + // Local maven repository is required for libraries built locally with maven like development jhipster-bom. + // mavenLocal() + mavenCentral() + //jhipster-needle-gradle-repositories - JHipster will add additional repositories +} + +dependencies { + // import JHipster dependencies BOM + if (!project.hasProperty("gae")) { + implementation platform("tech.jhipster:jhipster-dependencies:${jhipsterDependenciesVersion}") + } + + // Use ", version: jhipsterDependenciesVersion, changing: true" if you want + // to use a SNAPSHOT release instead of a stable release + implementation group: "tech.jhipster", name: "jhipster-framework" + implementation "javax.annotation:javax.annotation-api" + implementation "org.springframework.boot:spring-boot-starter-cache" + implementation "com.fasterxml.jackson.module:jackson-module-jaxb-annotations" + implementation "com.fasterxml.jackson.datatype:jackson-datatype-hibernate5" + implementation "com.fasterxml.jackson.datatype:jackson-datatype-hppc" + implementation "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" + testImplementation "org.testcontainers:junit-jupiter" + testImplementation "org.testcontainers:testcontainers" + implementation "org.springdoc:springdoc-openapi-webmvc-core" + implementation "com.zaxxer:HikariCP" + implementation "org.apache.commons:commons-lang3" + implementation "javax.cache:cache-api" + implementation "org.ehcache:ehcache" + implementation "org.hibernate:hibernate-jcache" + annotationProcessor "org.hibernate:hibernate-jpamodelgen:${hibernateVersion}" + implementation "org.hibernate:hibernate-core" + implementation "org.hibernate.validator:hibernate-validator" + implementation "org.liquibase:liquibase-core" + liquibaseRuntime "org.liquibase:liquibase-core" + implementation "org.mapstruct:mapstruct:${mapstructVersion}" + annotationProcessor "org.mapstruct:mapstruct-processor:${mapstructVersion}" + annotationProcessor "org.springframework.boot:spring-boot-configuration-processor:${springBootVersion}" + implementation "org.springframework.boot:spring-boot-loader-tools" + implementation "org.springframework.boot:spring-boot-starter-actuator" + implementation "org.springframework.boot:spring-boot-starter-data-jpa" + testImplementation "org.testcontainers:jdbc" + testImplementation "org.apache.commons:commons-collections4" + testImplementation "org.awaitility:awaitility:${awaitilityVersion}" + testImplementation "org.testcontainers:elasticsearch" + implementation "org.springframework.boot:spring-boot-starter-data-elasticsearch" + implementation "org.springframework.boot:spring-boot-starter-logging" + implementation "org.springframework.boot:spring-boot-starter-mail" + implementation "org.springframework.boot:spring-boot-starter-security" + implementation "org.springframework.boot:spring-boot-starter-thymeleaf" + implementation "org.springframework.boot:spring-boot-starter-web" + testImplementation "org.springframework.boot:spring-boot-starter-test" + testImplementation "org.springframework.boot:spring-boot-test" + testImplementation "org.springframework.security:spring-security-test" + testImplementation "com.tngtech.archunit:archunit-junit5-api:${archunitJunit5Version}" + testRuntimeOnly "com.tngtech.archunit:archunit-junit5-engine:${archunitJunit5Version}" + implementation "org.zalando:problem-spring-web" + implementation "org.springframework.boot:spring-boot-starter-undertow" + implementation "io.jsonwebtoken:jjwt-api" + if (!project.hasProperty("gae")) { + runtimeOnly "io.jsonwebtoken:jjwt-impl" + runtimeOnly "io.jsonwebtoken:jjwt-jackson" + } else { + implementation "io.jsonwebtoken:jjwt-impl" + implementation "io.jsonwebtoken:jjwt-jackson" + } + implementation "org.springframework.security:spring-security-data" + implementation "io.micrometer:micrometer-registry-prometheus" + implementation "io.dropwizard.metrics:metrics-core" + liquibaseRuntime sourceSets.main.compileClasspath + liquibaseRuntime "org.liquibase.ext:liquibase-hibernate5:${liquibaseHibernate5Version}" + //jhipster-needle-gradle-dependency - JHipster will add additional dependencies here +} + +if (project.hasProperty("gae")) { + task createPom { + def basePath = 'build/resources/main/META-INF/maven' + doLast { + pom { + withXml(dependencyManagement.pomConfigurer) + }.writeTo("${basePath}/${project.group}/${project.name}/pom.xml") + } + } + bootJar.dependsOn = [createPom] +} + +task cleanResources(type: Delete) { + delete "build/resources" +} + +wrapper { + gradleVersion = "7.4.2" +} + +task webapp_test(type: NpmTask, dependsOn: "npm_install") { + args = ["run", "webapp:test"] +} + +if (project.hasProperty("nodeInstall")) { + node { + version = "16.17.0" + npmVersion = "8.19.1" + download = true + } + + // Copy local node and npm to a fixed location for npmw + def fixedNode = tasks.register("fixedNode", Copy) { + from nodeSetup + into 'build/node' + } + tasks.named("nodeSetup").configure { finalizedBy fixedNode } + + def fixedNpm = tasks.register("fixedNpm", Copy) { + from npmSetup + into 'build/node' + } + tasks.named("npmSetup").configure { finalizedBy fixedNpm } +} + +test.dependsOn webapp_test +compileJava.dependsOn processResources +processResources.dependsOn bootBuildInfo diff --git a/checkstyle.xml b/checkstyle.xml new file mode 100644 index 00000000..4c6a0410 --- /dev/null +++ b/checkstyle.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + diff --git a/cypress.config.ts b/cypress.config.ts new file mode 100644 index 00000000..596afa48 --- /dev/null +++ b/cypress.config.ts @@ -0,0 +1,30 @@ +import { defineConfig } from 'cypress'; + +export const defaultConfig = { + video: false, + fixturesFolder: 'src/test/javascript/cypress/fixtures', + screenshotsFolder: 'build/cypress/screenshots', + downloadsFolder: 'build/cypress/downloads', + videosFolder: 'build/cypress/videos', + chromeWebSecurity: true, + viewportWidth: 1200, + viewportHeight: 720, + retries: 2, + env: { + authenticationUrl: '/api/authenticate', + jwtStorageName: 'jhi-authenticationToken', + }, + e2e: { + // We've imported your old cypress plugins here. + // You may want to clean this up later by importing these. + async setupNodeEvents(on, config) { + return (await import('./src/test/javascript/cypress/plugins/index')).default(on, config); + }, + baseUrl: 'http://localhost:8080/', + specPattern: 'src/test/javascript/cypress/e2e/**/*.cy.ts', + supportFile: 'src/test/javascript/cypress/support/index.ts', + experimentalSessionAndOrigin: true, + }, +}; + +export default defineConfig(defaultConfig); diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 00000000..c3d34da0 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,65 @@ +rootProject.name=twenty-one-points +profile=dev + +# Dependency versions +jhipsterDependenciesVersion=7.9.3 +# The spring-boot version should match the one managed by +# https://mvnrepository.com/artifact/tech.jhipster/jhipster-dependencies/7.9.3 +springBootVersion=2.7.3 +# The hibernate version should match the one managed by +# https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-dependencies/2.7.3 --> +hibernateVersion=5.6.10.Final +mapstructVersion=1.5.2.Final +archunitJunit5Version=0.22.0 +liquibaseHibernate5Version=4.15.0 +liquibaseTaskPrefix=liquibase + + + +jaxbRuntimeVersion=4.0.0 + +# gradle plugin version +jibPluginVersion=3.2.1 +gitPropertiesPluginVersion=2.4.1 +gradleNodePluginVersion=3.4.0 +liquibasePluginVersion=2.1.1 +sonarqubePluginVersion=3.4.0.2513 +noHttpCheckstyleVersion=0.0.10 +checkstyleVersion=10.3.2 +modernizerPluginVersion=1.6.2 +awaitilityVersion=4.2.0 + +# jhipster-needle-gradle-property - JHipster will add additional properties here + +## below are some of the gradle performance improvement settings that can be used as required, these are not enabled by default + +## The Gradle daemon aims to improve the startup and execution time of Gradle. +## The daemon is enabled by default in Gradle 3+ setting this to false will disable this. +## https://docs.gradle.org/current/userguide/gradle_daemon.html#sec:ways_to_disable_gradle_daemon +## uncomment the below line to disable the daemon + +#org.gradle.daemon=false + +## Specifies the JVM arguments used for the daemon process. +## The setting is particularly useful for tweaking memory settings. +## Default value: -Xmx1024m -XX:MaxPermSize=256m +## uncomment the below line to override the daemon defaults + +#org.gradle.jvmargs=-Xmx1024m -XX:MaxPermSize=256m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 + +## When configured, Gradle will run in incubating parallel mode. +## This option should only be used with decoupled projects. More details, visit +## http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +## uncomment the below line to enable parallel mode + +#org.gradle.parallel=true + +## Enables new incubating mode that makes Gradle selective when configuring projects. +## Only relevant projects are configured which results in faster builds for large multi-projects. +## http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:configuration_on_demand +## uncomment the below line to enable the selective mode + +#org.gradle.configureondemand=true + +## Install and use a local version of node and npm. +nodeInstall diff --git a/gradle/docker.gradle b/gradle/docker.gradle new file mode 100644 index 00000000..697d409b --- /dev/null +++ b/gradle/docker.gradle @@ -0,0 +1,29 @@ +jib { + from { + image = "eclipse-temurin:11-jre-focal" + platforms { + platform { + architecture = "${findProperty('jibArchitecture') ?: 'amd64'}" + os = "linux" + } + } + } + to { + image = "twentyonepoints:latest" + } + container { + entrypoint = ["bash", "-c", "/entrypoint.sh"] + ports = ["8080"] + environment = [ + SPRING_OUTPUT_ANSI_ENABLED: "ALWAYS", + JHIPSTER_SLEEP: "0" + ] + creationTime = "USE_CURRENT_TIMESTAMP" + user = 1000 + } + extraDirectories { + paths = file("src/main/docker/jib") + permissions = ["/entrypoint.sh": "755"] + } +} + diff --git a/gradle/profile_dev.gradle b/gradle/profile_dev.gradle new file mode 100644 index 00000000..62c8cd18 --- /dev/null +++ b/gradle/profile_dev.gradle @@ -0,0 +1,95 @@ +sourceSets { + test { + java { + exclude 'org/jhipster/health/config/PostgreSqlTestContainer.java' + } + } +} +dependencies { + developmentOnly "org.springframework.boot:spring-boot-devtools:${springBootVersion}" + implementation "com.h2database:h2" + testImplementation "org.testcontainers:postgresql" +} + +def profiles = "dev" +if (project.hasProperty("no-liquibase")) { + profiles += ",no-liquibase" +} +if (project.hasProperty("tls")) { + profiles += ",tls" +} + +springBoot { + buildInfo { + properties { + time = null + } + } +} + +bootRun { + args = ["--spring.profiles.active=${profiles}"] +} + +task webapp(type: NpmTask) { + inputs.property('appVersion', project.version) + inputs.files("package-lock.json") + .withPropertyName('package-lock') + .withPathSensitivity(PathSensitivity.RELATIVE) + inputs.files("build.gradle") + .withPropertyName('build.gradle') + .withPathSensitivity(PathSensitivity.RELATIVE) + inputs.files("angular.json") + .withPropertyName('angular.json') + .withPathSensitivity(PathSensitivity.RELATIVE) + inputs.files("tsconfig.json", "tsconfig.app.json") + .withPropertyName("tsconfig") + .withPathSensitivity(PathSensitivity.RELATIVE) + inputs.dir("webpack/") + .withPropertyName("webpack/") + .withPathSensitivity(PathSensitivity.RELATIVE) + inputs.dir("src/main/webapp/") + .withPropertyName("webapp-source-dir") + .withPathSensitivity(PathSensitivity.RELATIVE) + outputs.dir("build/resources/main/static/") + .withPropertyName("webapp-build-dir") + + dependsOn npmInstall + + args = ["run", "webapp:build"] + environment = [APP_VERSION: project.version] +} + +processResources { + inputs.property('version', version) + inputs.property('springProfiles', profiles) + filesMatching("**/application.yml") { + filter { + it.replace("#project.version#", version) + } + filter { + it.replace("#spring.profiles.active#", profiles) + } + } +} + +task integrationTest(type: Test) { + maxHeapSize = "1G" + useJUnitPlatform() + description = "Execute integration tests." + group = "verification" + include "**/*IT*", "**/*IntTest*" + testLogging { + events 'FAILED', 'SKIPPED' + } + systemProperty('spring.profiles.active', 'testdev') + systemProperty('java.security.egd', 'file:/dev/./urandom') + // uncomment if the tests reports are not generated + // see https://github.com/jhipster/generator-jhipster/pull/2771 and https://github.com/jhipster/generator-jhipster/pull/4484 + // ignoreFailures true + reports.html.enabled = false +} +integrationTest.dependsOn test + +processResources.dependsOn webapp +bootJar.dependsOn processResources diff --git a/gradle/profile_prod.gradle b/gradle/profile_prod.gradle new file mode 100644 index 00000000..72796654 --- /dev/null +++ b/gradle/profile_prod.gradle @@ -0,0 +1,66 @@ +dependencies { + implementation "org.postgresql:postgresql" + liquibaseRuntime "org.postgresql:postgresql" + testImplementation "org.testcontainers:postgresql" +} + +def profiles = "prod" +if (project.hasProperty("no-liquibase")) { + profiles += ",no-liquibase" +} + +if (project.hasProperty("api-docs")) { + profiles += ",api-docs" +} + +if (project.hasProperty("e2e")) { + profiles += ",e2e" +} + +springBoot { + buildInfo() +} + +bootRun { + args = ["--spring.profiles.active=${profiles}"] +} + +task webapp(type: NpmTask, dependsOn: "npm_install") { + args = ["run", "webapp:prod"] + environment = [APP_VERSION: project.version] +} + +processResources { + inputs.property('version', version) + inputs.property('springProfiles', profiles) + filesMatching("**/application.yml") { + filter { + it.replace("#project.version#", version) + } + filter { + it.replace("#spring.profiles.active#", profiles) + } + } +} + +task integrationTest(type: Test) { + maxHeapSize = "1G" + useJUnitPlatform() + description = "Execute integration tests." + group = "verification" + include "**/*IT*", "**/*IntTest*" + testLogging { + events 'FAILED', 'SKIPPED' + } + systemProperty('spring.profiles.active', 'testprod') + systemProperty('java.security.egd', 'file:/dev/./urandom') + // uncomment if the tests reports are not generated + // see https://github.com/jhipster/generator-jhipster/pull/2771 and https://github.com/jhipster/generator-jhipster/pull/4484 + // ignoreFailures true + reports.html.enabled = false +} +integrationTest.dependsOn test + + +processResources.dependsOn webapp +bootJar.dependsOn processResources diff --git a/gradle/sonar.gradle b/gradle/sonar.gradle new file mode 100644 index 00000000..bdd9c8a9 --- /dev/null +++ b/gradle/sonar.gradle @@ -0,0 +1,26 @@ +jacoco { + toolVersion = "0.8.8" +} + +jacocoTestReport { + executionData tasks.withType(Test) + classDirectories.from = files(sourceSets.main.output.classesDirs) + sourceDirectories.from = files(sourceSets.main.java.srcDirs) + + reports { + xml.enabled = true + } +} + +file("sonar-project.properties").withReader { + Properties sonarProperties = new Properties() + sonarProperties.load(it) + + sonarProperties.each { key, value -> + sonarqube { + properties { + property key, value + } + } + } +} diff --git a/gradle/war.gradle b/gradle/war.gradle new file mode 100644 index 00000000..5c01b4d0 --- /dev/null +++ b/gradle/war.gradle @@ -0,0 +1,16 @@ +apply plugin: "war" + +bootWar { + mainClass = "org.jhipster.health.TwentyOnePointsApp" + includes = ["WEB-INF/**", "META-INF/**"] + webXml = file("${project.rootDir}/src/main/webapp/WEB-INF/web.xml") +} + +war { + webAppDirName = "build/resources/main/static/" + webXml = file("${project.rootDir}/src/main/webapp/WEB-INF/web.xml") + enabled = true + archiveExtension = "war.original" + includes = ["WEB-INF/**", "META-INF/**"] + +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..41d9927a4d4fb3f96a785543079b8df6723c946b GIT binary patch literal 59821 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 literal 0 HcmV?d00001 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..aa991fce --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradle/zipkin.gradle b/gradle/zipkin.gradle new file mode 100644 index 00000000..82433b39 --- /dev/null +++ b/gradle/zipkin.gradle @@ -0,0 +1,3 @@ +dependencies { + implementation "org.springframework.cloud:spring-cloud-starter-zipkin" +} diff --git a/gradlew b/gradlew new file mode 100755 index 00000000..4f906e0c --- /dev/null +++ b/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 00000000..107acd32 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/jest.conf.js b/jest.conf.js new file mode 100644 index 00000000..a2a4d5a1 --- /dev/null +++ b/jest.conf.js @@ -0,0 +1,29 @@ +const { pathsToModuleNameMapper } = require('ts-jest'); + +const { + compilerOptions: { paths = {}, baseUrl = './' }, +} = require('./tsconfig.json'); +const environment = require('./webpack/environment'); + +module.exports = { + transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$|dayjs/esm)'], + resolver: 'jest-preset-angular/build/resolvers/ng-jest-resolver.js', + globals: { + ...environment, + }, + roots: ['', `/${baseUrl}`], + modulePaths: [`/${baseUrl}`], + setupFiles: ['jest-date-mock'], + cacheDirectory: '/build/jest-cache', + coverageDirectory: '/build/test-results/', + moduleNameMapper: pathsToModuleNameMapper(paths, { prefix: `/${baseUrl}/` }), + reporters: [ + 'default', + ['jest-junit', { outputDirectory: '/build/test-results/', outputName: 'TESTS-results-jest.xml' }], + ['jest-sonar', { outputDirectory: './build/test-results/jest', outputName: 'TESTS-results-sonar.xml' }], + ], + testMatch: ['/src/main/webapp/app/**/@(*.)@(spec.ts)'], + testEnvironmentOptions: { + url: 'https://jhipster.tech', + }, +}; diff --git a/ngsw-config.json b/ngsw-config.json new file mode 100644 index 00000000..8d576028 --- /dev/null +++ b/ngsw-config.json @@ -0,0 +1,21 @@ +{ + "$schema": "./node_modules/@angular/service-worker/config/schema.json", + "index": "/index.html", + "assetGroups": [ + { + "name": "app", + "installMode": "prefetch", + "resources": { + "files": ["/favicon.ico", "/index.html", "/manifest.webapp", "/*.css", "/*.js"] + } + }, + { + "name": "assets", + "installMode": "lazy", + "updateMode": "prefetch", + "resources": { + "files": ["/content/**", "/*.(eot|svg|cur|jpg|png|webp|gif|otf|ttf|woff|woff2|ani)"] + } + } + ] +} diff --git a/npmw b/npmw new file mode 100755 index 00000000..12a91e52 --- /dev/null +++ b/npmw @@ -0,0 +1,42 @@ +#!/bin/sh + +basedir=`dirname "$0"` + +if [ -f "$basedir/mvnw" ]; then + bindir="$basedir/target/node" + repodir="$basedir/target/node/node_modules" + installCommand="$basedir/mvnw -Pwebapp frontend:install-node-and-npm@install-node-and-npm" + + PATH="$basedir/$builddir/:$PATH" + NPM_EXE="$basedir/$builddir/node_modules/npm/bin/npm-cli.js" + NODE_EXE="$basedir/$builddir/node" +elif [ -f "$basedir/gradlew" ]; then + bindir="$basedir/build/node/bin" + repodir="$basedir/build/node/lib/node_modules" + installCommand="$basedir/gradlew npmSetup" +else + echo "Using npm installed globally" + exec npm "$@" +fi + +NPM_EXE="$repodir/npm/bin/npm-cli.js" +NODE_EXE="$bindir/node" + +if [ ! -x "$NPM_EXE" ] || [ ! -x "$NODE_EXE" ]; then + $installCommand || true +fi + +if [ -x "$NODE_EXE" ]; then + echo "Using node installed locally $($NODE_EXE --version)" + PATH="$bindir:$PATH" +else + NODE_EXE='node' +fi + +if [ ! -x "$NPM_EXE" ]; then + echo "Local npm not found, using npm installed globally" + npm "$@" +else + echo "Using npm installed locally $($NODE_EXE $NPM_EXE --version)" + $NODE_EXE $NPM_EXE "$@" +fi diff --git a/npmw.cmd b/npmw.cmd new file mode 100644 index 00000000..e45f868d --- /dev/null +++ b/npmw.cmd @@ -0,0 +1,31 @@ +@echo off + +setlocal + +set NPMW_DIR=%~dp0 + +if exist "%NPMW_DIR%mvnw.cmd" ( + set NODE_EXE=^"^" + set NODE_PATH=%NPMW_DIR%target\node\ + set NPM_EXE=^"%NPMW_DIR%target\node\npm.cmd^" + set INSTALL_NPM_COMMAND=^"%NPMW_DIR%mvnw.cmd^" -Pwebapp frontend:install-node-and-npm@install-node-and-npm +) else ( + set NODE_EXE=^"%NPMW_DIR%build\node\bin\node.exe^" + set NODE_PATH=%NPMW_DIR%build\node\bin\ + set NPM_EXE=^"%NPMW_DIR%build\node\lib\node_modules\npm\bin\npm-cli.js^" + set INSTALL_NPM_COMMAND=^"%NPMW_DIR%gradlew.bat^" npmSetup +) + +if not exist %NPM_EXE% ( + call %INSTALL_NPM_COMMAND% +) + +if exist %NODE_EXE% ( + Rem execute local npm with local node, whilst adding local node location to the PATH for this CMD session + endlocal & echo "%PATH%"|find /i "%NODE_PATH%;">nul || set "PATH=%NODE_PATH%;%PATH%" & call %NODE_EXE% %NPM_EXE% %* +) else if exist %NPM_EXE% ( + Rem execute local npm, whilst adding local npm location to the PATH for this CMD session + endlocal & echo "%PATH%"|find /i "%NODE_PATH%;">nul || set "PATH=%NODE_PATH%;%PATH%" & call %NPM_EXE% %* +) else ( + call npm %* +) diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..c32c9284 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,45855 @@ +{ + "name": "21-points", + "version": "7.0.2", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "21-points", + "version": "7.0.2", + "license": "Apache-2.0", + "dependencies": { + "@angular/animations": "14.2.0", + "@angular/common": "14.2.0", + "@angular/compiler": "14.2.0", + "@angular/core": "14.2.0", + "@angular/forms": "14.2.0", + "@angular/localize": "14.2.0", + "@angular/platform-browser": "14.2.0", + "@angular/platform-browser-dynamic": "14.2.0", + "@angular/router": "14.2.0", + "@fortawesome/angular-fontawesome": "0.11.1", + "@fortawesome/fontawesome-svg-core": "6.4.0", + "@fortawesome/free-solid-svg-icons": "6.2.0", + "@ng-bootstrap/ng-bootstrap": "13.1.1", + "@ngx-translate/core": "14.0.0", + "@ngx-translate/http-loader": "7.0.0", + "@popperjs/core": "2.11.6", + "angular-calendar": "0.30.1", + "bootstrap": "5.2.0", + "date-fns": "1.30.1", + "dayjs": "1.11.5", + "ng2-charts": "4.0.0", + "ngx-infinite-scroll": "14.0.0", + "ngx-webstorage": "10.0.1", + "rxjs": "7.5.6", + "tslib": "2.5.2", + "zone.js": "0.11.6" + }, + "devDependencies": { + "@angular-builders/custom-webpack": "14.1.0", + "@angular-builders/jest": "14.1.0", + "@angular-devkit/build-angular": "14.2.1", + "@angular-eslint/eslint-plugin": "14.0.3", + "@angular/cli": "14.2.1", + "@angular/compiler-cli": "14.2.0", + "@angular/service-worker": "14.2.0", + "@types/jest": "28.1.8", + "@types/node": "16.11.56", + "@typescript-eslint/eslint-plugin": "5.59.7", + "@typescript-eslint/parser": "5.59.1", + "browser-sync": "2.28.3", + "browser-sync-webpack-plugin": "2.3.0", + "concurrently": "8.2.0", + "copy-webpack-plugin": "11.0.0", + "cypress": "12.16.0", + "eslint": "8.42.0", + "eslint-config-prettier": "8.8.0", + "eslint-plugin-cypress": "2.12.1", + "eslint-webpack-plugin": "3.2.0", + "folder-hash": "4.0.2", + "generator-jhipster": "7.9.3", + "generator-jhipster-es-entity-reindexer": "1.0.4", + "husky": "8.0.3", + "jest": "28.1.3", + "jest-date-mock": "1.0.8", + "jest-environment-jsdom": "28.1.3", + "jest-junit": "14.0.1", + "jest-preset-angular": "12.2.2", + "jest-sonar": "0.2.12", + "lint-staged": "13.2.2", + "merge-jsons-webpack-plugin": "2.0.1", + "prettier": "2.8.8", + "prettier-plugin-java": "2.1.0", + "prettier-plugin-packagejson": "2.4.3", + "rimraf": "3.0.2", + "swagger-ui-dist": "4.19.0", + "ts-jest": "28.0.8", + "typescript": "4.8.2", + "wait-on": "7.0.1", + "webpack-bundle-analyzer": "4.6.1", + "webpack-merge": "5.9.0", + "webpack-notifier": "1.15.0" + }, + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/@adobe/css-tools": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.0.1.tgz", + "integrity": "sha512-+u76oB43nOHrF4DDWRLWDCtci7f3QJoEBigemIdIeTi1ODqjx6Tad9NCVnPRwewWlKkVab5PlK8DCtPTyX7S8g==", + "dev": true + }, + "node_modules/@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "dependencies": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@angular-builders/custom-webpack": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@angular-builders/custom-webpack/-/custom-webpack-14.1.0.tgz", + "integrity": "sha512-FLGDrBOg04cYvzCudeb15LWY2v91dtJ5+AfmP0aS/0T0D0AYmY4uM3FxZeh4jJcWETLvnHVFBCjan6y2Ct9J3A==", + "dev": true, + "dependencies": { + "@angular-devkit/architect": ">=0.1400.0 < 0.1500.0", + "@angular-devkit/build-angular": "^14.0.0", + "@angular-devkit/core": "^14.0.0", + "lodash": "^4.17.15", + "ts-node": "^10.0.0", + "tsconfig-paths": "^3.9.0", + "webpack-merge": "^5.7.3" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/compiler-cli": "^14.0.0" + } + }, + "node_modules/@angular-builders/custom-webpack/node_modules/@angular-devkit/build-angular": { + "version": "14.2.11", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-14.2.11.tgz", + "integrity": "sha512-O3X7GXcCBCGceVSHT+GIJ2JrRCg2YcO7HtNavpmPrraNr1o+aCdTkmT5WTS2cqWkZBm/z0wqKR8PsX/ZoD2r1A==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "2.2.0", + "@angular-devkit/architect": "0.1402.11", + "@angular-devkit/build-webpack": "0.1402.11", + "@angular-devkit/core": "14.2.11", + "@babel/core": "7.18.10", + "@babel/generator": "7.18.12", + "@babel/helper-annotate-as-pure": "7.18.6", + "@babel/plugin-proposal-async-generator-functions": "7.18.10", + "@babel/plugin-transform-async-to-generator": "7.18.6", + "@babel/plugin-transform-runtime": "7.18.10", + "@babel/preset-env": "7.18.10", + "@babel/runtime": "7.18.9", + "@babel/template": "7.18.10", + "@discoveryjs/json-ext": "0.5.7", + "@ngtools/webpack": "14.2.11", + "ansi-colors": "4.1.3", + "babel-loader": "8.2.5", + "babel-plugin-istanbul": "6.1.1", + "browserslist": "^4.9.1", + "cacache": "16.1.2", + "copy-webpack-plugin": "11.0.0", + "critters": "0.0.16", + "css-loader": "6.7.1", + "esbuild-wasm": "0.15.5", + "glob": "8.0.3", + "https-proxy-agent": "5.0.1", + "inquirer": "8.2.4", + "jsonc-parser": "3.1.0", + "karma-source-map-support": "1.4.0", + "less": "4.1.3", + "less-loader": "11.0.0", + "license-webpack-plugin": "4.0.2", + "loader-utils": "3.2.1", + "mini-css-extract-plugin": "2.6.1", + "minimatch": "5.1.0", + "open": "8.4.0", + "ora": "5.4.1", + "parse5-html-rewriting-stream": "6.0.1", + "piscina": "3.2.0", + "postcss": "8.4.16", + "postcss-import": "15.0.0", + "postcss-loader": "7.0.1", + "postcss-preset-env": "7.8.0", + "regenerator-runtime": "0.13.9", + "resolve-url-loader": "5.0.0", + "rxjs": "6.6.7", + "sass": "1.54.4", + "sass-loader": "13.0.2", + "semver": "7.3.7", + "source-map-loader": "4.0.0", + "source-map-support": "0.5.21", + "stylus": "0.59.0", + "stylus-loader": "7.0.0", + "terser": "5.14.2", + "text-table": "0.2.0", + "tree-kill": "1.2.2", + "tslib": "2.4.0", + "webpack": "5.76.1", + "webpack-dev-middleware": "5.3.3", + "webpack-dev-server": "4.11.0", + "webpack-merge": "5.8.0", + "webpack-subresource-integrity": "5.1.0" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "optionalDependencies": { + "esbuild": "0.15.5" + }, + "peerDependencies": { + "@angular/compiler-cli": "^14.0.0", + "@angular/localize": "^14.0.0", + "@angular/service-worker": "^14.0.0", + "karma": "^6.3.0", + "ng-packagr": "^14.0.0", + "protractor": "^7.0.0", + "tailwindcss": "^2.0.0 || ^3.0.0", + "typescript": ">=4.6.2 <4.9" + }, + "peerDependenciesMeta": { + "@angular/localize": { + "optional": true + }, + "@angular/service-worker": { + "optional": true + }, + "karma": { + "optional": true + }, + "ng-packagr": { + "optional": true + }, + "protractor": { + "optional": true + }, + "tailwindcss": { + "optional": true + } + } + }, + "node_modules/@angular-builders/custom-webpack/node_modules/@angular-devkit/build-webpack": { + "version": "0.1402.11", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1402.11.tgz", + "integrity": "sha512-Ajyg1O6B6JSHsDlPdh165uy3glW4IiUlRXu8VVAOSA88WIT1Dl17f4Oun0/t27ip0/CNceiVY9MzOqIwGL1E6g==", + "dev": true, + "dependencies": { + "@angular-devkit/architect": "0.1402.11", + "rxjs": "6.6.7" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "webpack": "^5.30.0", + "webpack-dev-server": "^4.0.0" + } + }, + "node_modules/@angular-builders/custom-webpack/node_modules/@ngtools/webpack": { + "version": "14.2.11", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-14.2.11.tgz", + "integrity": "sha512-4enbLFAp98uTgWYF6OFceQqLcfv2/0brIrNN4iWT9xe/Mh3zdCt+eH42zvNRsqo9WXNWRSLvnx8I924p83LNlw==", + "dev": true, + "engines": { + "node": "^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "@angular/compiler-cli": "^14.0.0", + "typescript": ">=4.6.2 <4.9", + "webpack": "^5.54.0" + } + }, + "node_modules/@angular-builders/custom-webpack/node_modules/connect-history-api-fallback": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/@angular-builders/custom-webpack/node_modules/loader-utils": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz", + "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==", + "dev": true, + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/@angular-builders/custom-webpack/node_modules/postcss-import": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.0.0.tgz", + "integrity": "sha512-Y20shPQ07RitgBGv2zvkEAu9bqvrD77C9axhj/aA1BQj4czape2MdClCExvB27EwYEJdGgKZBpKanb0t1rK2Kg==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/@angular-builders/custom-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-builders/custom-webpack/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-builders/custom-webpack/node_modules/schema-utils": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.1.tgz", + "integrity": "sha512-lELhBAAly9NowEsX0yZBlw9ahZG+sK/1RJ21EpzdYHKEs13Vku3LJ+MIPhh4sMs0oCCeufZQEQbMekiA4vuVIQ==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/@angular-builders/custom-webpack/node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "dev": true + }, + "node_modules/@angular-builders/custom-webpack/node_modules/webpack-dev-server": { + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.11.0.tgz", + "integrity": "sha512-L5S4Q2zT57SK7tazgzjMiSMBdsw+rGYIX27MgPgx7LDhWO0lViPrHKoLS7jo5In06PWYAhlYu3PbyoC6yAThbw==", + "dev": true, + "dependencies": { + "@types/bonjour": "^3.5.9", + "@types/connect-history-api-fallback": "^1.3.5", + "@types/express": "^4.17.13", + "@types/serve-index": "^1.9.1", + "@types/serve-static": "^1.13.10", + "@types/sockjs": "^0.3.33", + "@types/ws": "^8.5.1", + "ansi-html-community": "^0.0.8", + "bonjour-service": "^1.0.11", + "chokidar": "^3.5.3", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^2.0.0", + "default-gateway": "^6.0.3", + "express": "^4.17.3", + "graceful-fs": "^4.2.6", + "html-entities": "^2.3.2", + "http-proxy-middleware": "^2.0.3", + "ipaddr.js": "^2.0.1", + "open": "^8.0.9", + "p-retry": "^4.5.0", + "rimraf": "^3.0.2", + "schema-utils": "^4.0.0", + "selfsigned": "^2.0.1", + "serve-index": "^1.9.1", + "sockjs": "^0.3.24", + "spdy": "^4.0.2", + "webpack-dev-middleware": "^5.3.1", + "ws": "^8.4.2" + }, + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.37.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/@angular-builders/custom-webpack/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/@angular-builders/jest": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@angular-builders/jest/-/jest-14.1.0.tgz", + "integrity": "sha512-uGXyJ40F7HXfoWwW1EK5U75sUUQ4zVireLYPUYWT1/t1vqizss0mIPR026bMWwScWJhetdopxlWcql4wfNuXyQ==", + "dev": true, + "dependencies": { + "@angular-devkit/architect": ">=0.1400.0 < 0.1500.0", + "@angular-devkit/core": "^14.0.0", + "jest-preset-angular": "12.2.2", + "lodash": "^4.17.15" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular-devkit/build-angular": "^14.0.0", + "@angular/compiler-cli": "^14.0.0", + "@angular/core": "^14.0.0", + "@angular/platform-browser-dynamic": "^14.0.0", + "jest": ">=28" + } + }, + "node_modules/@angular-devkit/architect": { + "version": "0.1402.11", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1402.11.tgz", + "integrity": "sha512-RuSZrBQ+QbipAESZ4aXCyAMQHaEaDyyV/FDS9J2HJWfEFbRD5oxlEt/tBC8XjmJQsktaUOh07GT8MNJjPKVAQw==", + "dev": true, + "dependencies": { + "@angular-devkit/core": "14.2.11", + "rxjs": "6.6.7" + }, + "engines": { + "node": "^14.15.0 || >=16.10.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": "14.2.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-14.2.1.tgz", + "integrity": "sha512-6FeIe2nsNH/fslZmTfbo+RCjLk3HuCQa5rb/SFf3w0mwzWCrhV0/SqnQ4+YYgkKrohmTX+q5JmJPwWnkqgdIgQ==", + "dev": true, + "dependencies": { + "@ampproject/remapping": "2.2.0", + "@angular-devkit/architect": "0.1402.1", + "@angular-devkit/build-webpack": "0.1402.1", + "@angular-devkit/core": "14.2.1", + "@babel/core": "7.18.10", + "@babel/generator": "7.18.12", + "@babel/helper-annotate-as-pure": "7.18.6", + "@babel/plugin-proposal-async-generator-functions": "7.18.10", + "@babel/plugin-transform-async-to-generator": "7.18.6", + "@babel/plugin-transform-runtime": "7.18.10", + "@babel/preset-env": "7.18.10", + "@babel/runtime": "7.18.9", + "@babel/template": "7.18.10", + "@discoveryjs/json-ext": "0.5.7", + "@ngtools/webpack": "14.2.1", + "ansi-colors": "4.1.3", + "babel-loader": "8.2.5", + "babel-plugin-istanbul": "6.1.1", + "browserslist": "^4.9.1", + "cacache": "16.1.2", + "copy-webpack-plugin": "11.0.0", + "critters": "0.0.16", + "css-loader": "6.7.1", + "esbuild-wasm": "0.15.5", + "glob": "8.0.3", + "https-proxy-agent": "5.0.1", + "inquirer": "8.2.4", + "jsonc-parser": "3.1.0", + "karma-source-map-support": "1.4.0", + "less": "4.1.3", + "less-loader": "11.0.0", + "license-webpack-plugin": "4.0.2", + "loader-utils": "3.2.0", + "mini-css-extract-plugin": "2.6.1", + "minimatch": "5.1.0", + "open": "8.4.0", + "ora": "5.4.1", + "parse5-html-rewriting-stream": "6.0.1", + "piscina": "3.2.0", + "postcss": "8.4.16", + "postcss-import": "14.1.0", + "postcss-loader": "7.0.1", + "postcss-preset-env": "7.8.0", + "regenerator-runtime": "0.13.9", + "resolve-url-loader": "5.0.0", + "rxjs": "6.6.7", + "sass": "1.54.4", + "sass-loader": "13.0.2", + "semver": "7.3.7", + "source-map-loader": "4.0.0", + "source-map-support": "0.5.21", + "stylus": "0.59.0", + "stylus-loader": "7.0.0", + "terser": "5.14.2", + "text-table": "0.2.0", + "tree-kill": "1.2.2", + "tslib": "2.4.0", + "webpack": "5.74.0", + "webpack-dev-middleware": "5.3.3", + "webpack-dev-server": "4.10.0", + "webpack-merge": "5.8.0", + "webpack-subresource-integrity": "5.1.0" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "optionalDependencies": { + "esbuild": "0.15.5" + }, + "peerDependencies": { + "@angular/compiler-cli": "^14.0.0", + "@angular/localize": "^14.0.0", + "@angular/service-worker": "^14.0.0", + "karma": "^6.3.0", + "ng-packagr": "^14.0.0", + "protractor": "^7.0.0", + "tailwindcss": "^2.0.0 || ^3.0.0", + "typescript": ">=4.6.2 <4.9" + }, + "peerDependenciesMeta": { + "@angular/localize": { + "optional": true + }, + "@angular/service-worker": { + "optional": true + }, + "karma": { + "optional": true + }, + "ng-packagr": { + "optional": true + }, + "protractor": { + "optional": true + }, + "tailwindcss": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/@angular-devkit/architect": { + "version": "0.1402.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1402.1.tgz", + "integrity": "sha512-OZ5mkVHSrk5WX6wIfvkxJUCqJRtfQh1S476qHIi80llhJufFTwoOwJrcz7XtNRvbZMLRMztf0aIhEDaUtEBRZQ==", + "dev": true, + "dependencies": { + "@angular-devkit/core": "14.2.1", + "rxjs": "6.6.7" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular-devkit/build-angular/node_modules/@angular-devkit/core": { + "version": "14.2.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-14.2.1.tgz", + "integrity": "sha512-lW8oNGuJqr4r31FWBjfWQYkSXdiOHBGOThIEtHvUVBKfPF/oVrupLueCUgBPel+NvxENXdo93uPsqHN7bZbmsQ==", + "dev": true, + "dependencies": { + "ajv": "8.11.0", + "ajv-formats": "2.1.1", + "jsonc-parser": "3.1.0", + "rxjs": "6.6.7", + "source-map": "0.7.4" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^3.5.2" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": 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/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "dev": true + }, + "node_modules/@angular-devkit/build-angular/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/@angular-devkit/build-webpack": { + "version": "0.1402.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1402.1.tgz", + "integrity": "sha512-S9WvNKrHVgOWNqGCytJNPBEMojB9j6cgl5XlUMEQ+4NV5geOlDNiZltQ/gbC1jz19OU5N+Ky374mPI5IeIBqZg==", + "dev": true, + "dependencies": { + "@angular-devkit/architect": "0.1402.1", + "rxjs": "6.6.7" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "webpack": "^5.30.0", + "webpack-dev-server": "^4.0.0" + } + }, + "node_modules/@angular-devkit/build-webpack/node_modules/@angular-devkit/architect": { + "version": "0.1402.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1402.1.tgz", + "integrity": "sha512-OZ5mkVHSrk5WX6wIfvkxJUCqJRtfQh1S476qHIi80llhJufFTwoOwJrcz7XtNRvbZMLRMztf0aIhEDaUtEBRZQ==", + "dev": true, + "dependencies": { + "@angular-devkit/core": "14.2.1", + "rxjs": "6.6.7" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular-devkit/build-webpack/node_modules/@angular-devkit/core": { + "version": "14.2.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-14.2.1.tgz", + "integrity": "sha512-lW8oNGuJqr4r31FWBjfWQYkSXdiOHBGOThIEtHvUVBKfPF/oVrupLueCUgBPel+NvxENXdo93uPsqHN7bZbmsQ==", + "dev": true, + "dependencies": { + "ajv": "8.11.0", + "ajv-formats": "2.1.1", + "jsonc-parser": "3.1.0", + "rxjs": "6.6.7", + "source-map": "0.7.4" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^3.5.2" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "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": "14.2.11", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-14.2.11.tgz", + "integrity": "sha512-cBIGs6y9rykOQqnuAQOB1DgIRyBFYtvKRJb7QNUfIJ0qUfARKkuV/yikv3lrb95ePGkmoRzmjkFqcFZiYU+r7A==", + "dev": true, + "dependencies": { + "ajv": "8.11.0", + "ajv-formats": "2.1.1", + "jsonc-parser": "3.1.0", + "rxjs": "6.6.7", + "source-map": "0.7.4" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^3.5.2" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "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-devkit/schematics": { + "version": "14.2.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-14.2.1.tgz", + "integrity": "sha512-0U18FwDYt4zROBPrvewH6iBTkf2ozVHN4/gxUb9jWrqVw8mPU5AWc/iYxQLHBSinkr2Egjo1H/i9aBqgJSeh3g==", + "dev": true, + "dependencies": { + "@angular-devkit/core": "14.2.1", + "jsonc-parser": "3.1.0", + "magic-string": "0.26.2", + "ora": "5.4.1", + "rxjs": "6.6.7" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular-devkit/schematics/node_modules/@angular-devkit/core": { + "version": "14.2.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-14.2.1.tgz", + "integrity": "sha512-lW8oNGuJqr4r31FWBjfWQYkSXdiOHBGOThIEtHvUVBKfPF/oVrupLueCUgBPel+NvxENXdo93uPsqHN7bZbmsQ==", + "dev": true, + "dependencies": { + "ajv": "8.11.0", + "ajv-formats": "2.1.1", + "jsonc-parser": "3.1.0", + "rxjs": "6.6.7", + "source-map": "0.7.4" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^3.5.2" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/@angular-devkit/schematics/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/schematics/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-eslint/bundled-angular-compiler": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-14.0.3.tgz", + "integrity": "sha512-1JMdb5IrgFH+bxY8ZYXyNyu31+BELbahGKOi+jX0ZyZgGA8nbukzjL+QVNwwaw1RNuyiCxZzCwA/tRSGJMqSYA==", + "dev": true + }, + "node_modules/@angular-eslint/eslint-plugin": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin/-/eslint-plugin-14.0.3.tgz", + "integrity": "sha512-s3rgibA+sFcDswpKUXbdykOBT7KoSk205bIBARJrIZr0zR7zsPLS/XQQGk83VICeAgcGHSPlNBmspOBrLfihEQ==", + "dev": true, + "dependencies": { + "@angular-eslint/utils": "14.0.3", + "@typescript-eslint/utils": "5.29.0" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0", + "typescript": "*" + } + }, + "node_modules/@angular-eslint/utils": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/@angular-eslint/utils/-/utils-14.0.3.tgz", + "integrity": "sha512-horyyq6shximqacDhHm6GhEJI71dvGfr2iiqP/PtT944dhY+Ml6fj85H9ywKQxd5ENYPBKrzOGCBxpLyQU07Ow==", + "dev": true, + "dependencies": { + "@angular-eslint/bundled-angular-compiler": "14.0.3", + "@typescript-eslint/utils": "5.29.0" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0", + "typescript": "*" + } + }, + "node_modules/@angular/animations": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-14.2.0.tgz", + "integrity": "sha512-T54F5NvX0XMj6L7hdqYYORFjY6EckOWaK96bF9QqJLKwIRViCEGR5YzaHGhQaAipvUJ+kEwN+bnRBJWOaNbmUw==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/core": "14.2.0" + } + }, + "node_modules/@angular/cli": { + "version": "14.2.1", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-14.2.1.tgz", + "integrity": "sha512-ab/kpK3wYQvDOdhwfk3cVSiLMyl9lEVrNiwvrTnPLdt3jwqkT5Gm28WFShnOuNCaKea3iHP7LIItoRxeIWQQ9A==", + "dev": true, + "dependencies": { + "@angular-devkit/architect": "0.1402.1", + "@angular-devkit/core": "14.2.1", + "@angular-devkit/schematics": "14.2.1", + "@schematics/angular": "14.2.1", + "@yarnpkg/lockfile": "1.1.0", + "ansi-colors": "4.1.3", + "debug": "4.3.4", + "ini": "3.0.0", + "inquirer": "8.2.4", + "jsonc-parser": "3.1.0", + "npm-package-arg": "9.1.0", + "npm-pick-manifest": "7.0.1", + "open": "8.4.0", + "ora": "5.4.1", + "pacote": "13.6.2", + "resolve": "1.22.1", + "semver": "7.3.7", + "symbol-observable": "4.0.0", + "uuid": "8.3.2", + "yargs": "17.5.1" + }, + "bin": { + "ng": "bin/ng.js" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular/cli/node_modules/@angular-devkit/architect": { + "version": "0.1402.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1402.1.tgz", + "integrity": "sha512-OZ5mkVHSrk5WX6wIfvkxJUCqJRtfQh1S476qHIi80llhJufFTwoOwJrcz7XtNRvbZMLRMztf0aIhEDaUtEBRZQ==", + "dev": true, + "dependencies": { + "@angular-devkit/core": "14.2.1", + "rxjs": "6.6.7" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@angular/cli/node_modules/@angular-devkit/core": { + "version": "14.2.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-14.2.1.tgz", + "integrity": "sha512-lW8oNGuJqr4r31FWBjfWQYkSXdiOHBGOThIEtHvUVBKfPF/oVrupLueCUgBPel+NvxENXdo93uPsqHN7bZbmsQ==", + "dev": true, + "dependencies": { + "ajv": "8.11.0", + "ajv-formats": "2.1.1", + "jsonc-parser": "3.1.0", + "rxjs": "6.6.7", + "source-map": "0.7.4" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^3.5.2" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/@angular/cli/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/cli/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/common": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-14.2.0.tgz", + "integrity": "sha512-dXCGuM/Yh9B8OVVHHfPuXLYdu0olemKmuYLZXGoqyzeYU5OpQ6+qcHYTrlREkWpF9QY+pk2ewfM9Hke1UENEHA==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/core": "14.2.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/compiler": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-14.2.0.tgz", + "integrity": "sha512-csnvopy5a6ctdT7sHgwgn3wRni+UIk2zvs7WYsx+gtLTdH7SPg78CN0XZR/KQ142qLtuMnmkVON3D3ejmqC9xg==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/core": "14.2.0" + }, + "peerDependenciesMeta": { + "@angular/core": { + "optional": true + } + } + }, + "node_modules/@angular/compiler-cli": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-14.2.0.tgz", + "integrity": "sha512-cMjMVmAitY4GIn45pIqzhy4GKWfVAJSktCtuAebaAYC6Nqo9LbvqF2dnHK0MHoqjAf98e5zmz9WeBOQ7YlRZFQ==", + "dependencies": { + "@babel/core": "^7.17.2", + "chokidar": "^3.0.0", + "convert-source-map": "^1.5.1", + "dependency-graph": "^0.11.0", + "magic-string": "^0.26.0", + "reflect-metadata": "^0.1.2", + "semver": "^7.0.0", + "sourcemap-codec": "^1.4.8", + "tslib": "^2.3.0", + "yargs": "^17.2.1" + }, + "bin": { + "ng-xi18n": "bundles/src/bin/ng_xi18n.js", + "ngc": "bundles/src/bin/ngc.js", + "ngcc": "bundles/ngcc/main-ngcc.js" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/compiler": "14.2.0", + "typescript": ">=4.6.2 <4.9" + } + }, + "node_modules/@angular/core": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-14.2.0.tgz", + "integrity": "sha512-m00WZL+df90QjrNmA43W8oSP1hyePoEXgw36JcFLQ+2ISO1HvqoeuKXIccyIQKpCuQ9VLNv5ptlTio6pjmcCqg==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "rxjs": "^6.5.3 || ^7.4.0", + "zone.js": "~0.11.4" + } + }, + "node_modules/@angular/forms": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-14.2.0.tgz", + "integrity": "sha512-KVAd4ITEUnP/MLNTDbTWEMoYIZgKawvoaNu4nBP0DAcZsADmB5a6IPYcg6GrgJ+teN0SnpiCxKvq1hRITj3d7g==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/common": "14.2.0", + "@angular/core": "14.2.0", + "@angular/platform-browser": "14.2.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/localize": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@angular/localize/-/localize-14.2.0.tgz", + "integrity": "sha512-sV+4itOllkgYkHMMtfYGsOvO+nuFMBlk6RKFVqWZcFO+XgOC4E9bXhRbkpGZ0JcIr42tlB63krjopnYfN9u+7A==", + "dependencies": { + "@babel/core": "7.18.9", + "glob": "8.0.3", + "yargs": "^17.2.1" + }, + "bin": { + "localize-extract": "tools/bundles/src/extract/cli.js", + "localize-migrate": "tools/bundles/src/migrate/cli.js", + "localize-translate": "tools/bundles/src/translate/cli.js" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/compiler": "14.2.0", + "@angular/compiler-cli": "14.2.0" + } + }, + "node_modules/@angular/localize/node_modules/@babel/core": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.9.tgz", + "integrity": "sha512-1LIb1eL8APMy91/IMW+31ckrfBM4yCoLaVzoDhZUKSM4cu1L1nIidyxkCgzPAgrC5WEz36IPEr/eSeSF9pIn+g==", + "dependencies": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.18.9", + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-module-transforms": "^7.18.9", + "@babel/helpers": "^7.18.9", + "@babel/parser": "^7.18.9", + "@babel/template": "^7.18.6", + "@babel/traverse": "^7.18.9", + "@babel/types": "^7.18.9", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@angular/localize/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@angular/platform-browser": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-14.2.0.tgz", + "integrity": "sha512-zYWB9FtZQZEhPV2PuzaoXWChWS+7o4SP+0sF8hLddGNOezf1FsKKrqDhyYtQYgZ2qQPsS+uDFfkVry5dppALUQ==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/animations": "14.2.0", + "@angular/common": "14.2.0", + "@angular/core": "14.2.0" + }, + "peerDependenciesMeta": { + "@angular/animations": { + "optional": true + } + } + }, + "node_modules/@angular/platform-browser-dynamic": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-14.2.0.tgz", + "integrity": "sha512-CRSWbJiohfz4KBU4ts2kq2B2lATApMeG6uuxgXwNJAEUkn63EYbrFVY9JTaApyZ7X+3dUDZZtcsfNmQruK2UxQ==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/common": "14.2.0", + "@angular/compiler": "14.2.0", + "@angular/core": "14.2.0", + "@angular/platform-browser": "14.2.0" + } + }, + "node_modules/@angular/router": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-14.2.0.tgz", + "integrity": "sha512-SvtmSlRCIWeLhbXYh09W7luPKTtpZL16PB4DbT3VVfZlt2PfMys/tAmOCvChNxjkOgV7yEGaqklWw/1wKxeRlg==", + "dependencies": { + "tslib": "^2.3.0" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/common": "14.2.0", + "@angular/core": "14.2.0", + "@angular/platform-browser": "14.2.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@angular/service-worker": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@angular/service-worker/-/service-worker-14.2.0.tgz", + "integrity": "sha512-9lX+YZyeO02nOUkhbHo/S2YardiVVpmTr4UKa2v7uQUauASOAh1haPDBQpHyiEN6TIXMMJ0bawiDPFcNPrvU8g==", + "dev": true, + "dependencies": { + "tslib": "^2.3.0" + }, + "bin": { + "ngsw-config": "ngsw-config.js" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0" + }, + "peerDependencies": { + "@angular/common": "14.2.0", + "@angular/core": "14.2.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.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "dependencies": { + "@babel/highlight": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.19.0.tgz", + "integrity": "sha512-y5rqgTTPTmaF5e2nVhOxw+Ur9HDJLsWb6U/KpgUzRZEdPfE6VOubXBKLdbcUTijzRptednSBDQbYZBOSqJxpJw==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.10.tgz", + "integrity": "sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw==", + "dependencies": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.18.10", + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-module-transforms": "^7.18.9", + "@babel/helpers": "^7.18.9", + "@babel/parser": "^7.18.10", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.18.10", + "@babel/types": "^7.18.10", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.1", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.18.12", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.12.tgz", + "integrity": "sha512-dfQ8ebCN98SvyL7IxNMCUtZQSq5R7kxgN+r8qYTGDmmSion1hX2C0zq2yo1bsCDhXixokv1SAWTZUMYbO/V5zg==", + "dependencies": { + "@babel/types": "^7.18.10", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", + "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz", + "integrity": "sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw==", + "dev": true, + "dependencies": { + "@babel/helper-explode-assignable-expression": "^7.18.6", + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.0.tgz", + "integrity": "sha512-Ai5bNWXIvwDvWM7njqsG3feMlL9hCVQsPYXodsZyLwshYkZVJt59Gftau4VrE8S9IT9asd2uSP1hG6wCNw+sXA==", + "dependencies": { + "@babel/compat-data": "^7.19.0", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.20.2", + "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.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.19.0.tgz", + "integrity": "sha512-NRz8DwF4jT3UfrmUoZjd0Uph9HQnP30t7Ash+weACcyNkiYTywpIjDBgReJMKgr+n86sn2nPVVmJ28Dm053Kqw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-member-expression-to-functions": "^7.18.9", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-replace-supers": "^7.18.9", + "@babel/helper-split-export-declaration": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-create-regexp-features-plugin": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.19.0.tgz", + "integrity": "sha512-htnV+mHX32DF81amCDrwIDr8nrp1PTm+3wfBN9/v8QJOLEioOCOG7qNyq0nHeFiWbT3Eb7gsPwEmV64UCQ1jzw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "regexpu-core": "^5.1.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-define-polyfill-provider": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.2.tgz", + "integrity": "sha512-r9QJJ+uDWrd+94BSPcP6/de67ygLtvVy6cK4luE6MOuDsZIdoaPBnfSpbO/+LTifjPckbKXRuI9BB/Z2/y3iTg==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.17.7", + "@babel/helper-plugin-utils": "^7.16.7", + "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.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-environment-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", + "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-explode-assignable-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz", + "integrity": "sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", + "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", + "dependencies": { + "@babel/template": "^7.18.10", + "@babel/types": "^7.19.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz", + "integrity": "sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.19.0.tgz", + "integrity": "sha512-3HBZ377Fe14RbLIA+ac3sY4PTgpxHVkFrESaWhoI5PuyXPBBX8+C34qblV9G89ZtycGJCmCI/Ut+VUDK4bltNQ==", + "dependencies": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.18.6", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.19.0", + "@babel/types": "^7.19.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz", + "integrity": "sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.19.0.tgz", + "integrity": "sha512-40Ryx7I8mT+0gaNxm8JGTZFUITNqdLAgdg0hXzeVZxVD6nFsdhQvip6v8dqkRHzsz1VFpFAaOCHNn0vKBL7Czw==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-remap-async-to-generator": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz", + "integrity": "sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-wrap-function": "^7.18.9", + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-replace-supers": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.18.9.tgz", + "integrity": "sha512-dNsWibVI4lNT6HiuOIBr1oyxo40HvIVmbwPUm3XZ7wMh4k2WxrxTqZwSqw/eEmXDS9np0ey5M2bz9tBmO9c+YQ==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-member-expression-to-functions": "^7.18.9", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/traverse": "^7.18.9", + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz", + "integrity": "sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.18.9.tgz", + "integrity": "sha512-imytd2gHi3cJPsybLRbmFrF7u5BIEuI2cNheyKi3/iOBC63kNn3q8Crn2xVuESli0aM4KYsyEqKyS7lFL8YVtw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz", + "integrity": "sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", + "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", + "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-wrap-function": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.19.0.tgz", + "integrity": "sha512-txX8aN8CZyYGTwcLhlk87KRqncAzhh5TpQamZUa0/u3an36NtDpUP6bQgBCBcLeBs09R/OwQu3OjK0k/HwfNDg==", + "dev": true, + "dependencies": { + "@babel/helper-function-name": "^7.19.0", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.19.0", + "@babel/types": "^7.19.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.19.0.tgz", + "integrity": "sha512-DRBCKGwIEdqY3+rPJgG/dKfQy9+08rHIAJx8q2p+HSWP87s2HCrQmaAMMyMll2kIXKCW0cO1RdQskx15Xakftg==", + "dependencies": { + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.19.0", + "@babel/types": "^7.19.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "dependencies": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.19.0.tgz", + "integrity": "sha512-74bEXKX2h+8rrfQUfsBfuZZHzsEs6Eql4pqy/T4Nn6Y9wNPggQOqD6z6pn5Bl8ZfysKouFZT/UXEH94ummEeQw==", + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz", + "integrity": "sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.18.9.tgz", + "integrity": "sha512-AHrP9jadvH7qlOj6PINbgSuphjQUAK7AOT7DPjBo9EHoLhQTnnK5u45e1Hd4DbSQEO9nqPWtQ89r+XEOWFScKg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", + "@babel/plugin-proposal-optional-chaining": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.13.0" + } + }, + "node_modules/@babel/plugin-proposal-async-generator-functions": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.18.10.tgz", + "integrity": "sha512-1mFuY2TOsR1hxbjCo4QL+qlIjV07p4H4EUYw2J/WCqsvFV6V9X9z9YhXbWndc/4fw+hYGlDT7egYxliMp5O6Ew==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-remap-async-to-generator": "^7.18.9", + "@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.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", + "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-class-static-block": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.18.6.tgz", + "integrity": "sha512-+I3oIiNxrCpup3Gi8n5IGMwj0gOCAjcJUSQEcotNnCCPMEnixawOQ+KeJPlgfjzx+FKQ1QSyZOWe7wmoJp7vhw==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@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.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz", + "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@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.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", + "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9", + "@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.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz", + "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@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.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.18.9.tgz", + "integrity": "sha512-128YbMpjCrP35IOExw2Fq+x55LMP42DzhOhX2aNNIdI9avSWl2PI0yuBWarr3RYpZBSPtabfadkH2yeRiMD61Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9", + "@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.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", + "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@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.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", + "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@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.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.18.9.tgz", + "integrity": "sha512-kDDHQ5rflIeY5xl69CEqGEZ0KY369ehsCIEbTGb4siHG5BE9sga/T0r0OUwyZNLMmZE79E1kbsqAjwFCW4ds6Q==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.18.8", + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.18.8" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-optional-catch-binding": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", + "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@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.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.18.9.tgz", + "integrity": "sha512-v5nwt4IqBXihxGsW2QmCWMDS3B3bzGIk/EQVZz2ei7f3NJl8NzAJVvUmpDW5q1CRNY+Beb/k58UAH1Km1N411w==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", + "@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.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", + "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", + "dev": true, + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-proposal-private-property-in-object": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.18.6.tgz", + "integrity": "sha512-9Rysx7FOctvT5ouj5JODjAFAkgGoudQuLPamZb0v1TGLpapdNaftzifU8NTWQm0IRjqoYypdrSmyWgkocDQ8Dw==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@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-unicode-property-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", + "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "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-assertions": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.18.6.tgz", + "integrity": "sha512-/DU3RXad9+bZwrgWJQKbr39gYbJpLJHezqEzRzi/BHRlJ9zsQb4CK2CA/5apllXNomwA1qHwzvHl+AdEmC5krQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "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.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.18.6.tgz", + "integrity": "sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-arrow-functions": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.18.6.tgz", + "integrity": "sha512-9S9X9RUefzrsHZmKMbDXxweEH+YlE8JJEuat9FdvW9Qh1cw7W64jELCtWNkPBPX5En45uy28KGvA/AySqUh8CQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-async-to-generator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.18.6.tgz", + "integrity": "sha512-ARE5wZLKnTgPW7/1ftQmSi1CmkqqHo2DNmtztFhvgtOWSDfq0Cq9/9L+KnZNYSNrydBekhW3rwShduf59RoXag==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-remap-async-to-generator": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoped-functions": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz", + "integrity": "sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-block-scoping": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.18.9.tgz", + "integrity": "sha512-5sDIJRV1KtQVEbt/EIBwGy4T01uYIo4KRB3VUqzkhrAIOGx7AoctL9+Ux88btY0zXdDyPJ9mW+bg+v+XEkGmtw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-classes": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.19.0.tgz", + "integrity": "sha512-YfeEE9kCjqTS9IitkgfJuxjcEtLUHMqa8yUJ6zdz8vR7hKuo6mOy2C05P0F1tdMmDCeuyidKnlrw/iTppHcr2A==", + "dev": true, + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-compilation-targets": "^7.19.0", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-replace-supers": "^7.18.9", + "@babel/helper-split-export-declaration": "^7.18.6", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-computed-properties": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.18.9.tgz", + "integrity": "sha512-+i0ZU1bCDymKakLxn5srGHrsAPRELC2WIbzwjLhHW9SIE1cPYkLCL0NlnXMZaM1vhfgA2+M7hySk42VBvrkBRw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-destructuring": { + "version": "7.18.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.18.13.tgz", + "integrity": "sha512-TodpQ29XekIsex2A+YJPj5ax2plkGa8YYY6mFjCohk/IG9IY42Rtuj1FuDeemfg2ipxIFLzPeA83SIBnlhSIow==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-dotall-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz", + "integrity": "sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-duplicate-keys": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz", + "integrity": "sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-exponentiation-operator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz", + "integrity": "sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==", + "dev": true, + "dependencies": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-for-of": { + "version": "7.18.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz", + "integrity": "sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-function-name": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz", + "integrity": "sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==", + "dev": true, + "dependencies": { + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-literals": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz", + "integrity": "sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-member-expression-literals": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz", + "integrity": "sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-modules-amd": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.18.6.tgz", + "integrity": "sha512-Pra5aXsmTsOnjM3IajS8rTaLCy++nGM4v3YR4esk5PCsyg9z8NA5oQLwxzMUtDBd8F+UmVza3VxoAaWCbzH1rg==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "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.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.18.6.tgz", + "integrity": "sha512-Qfv2ZOWikpvmedXQJDSbxNqy7Xr/j2Y8/KfijM0iJyKkBTmWuvCA1yeH1yDM7NJhBW/2aXxeucLj6i80/LAJ/Q==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-simple-access": "^7.18.6", + "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.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.19.0.tgz", + "integrity": "sha512-x9aiR0WXAWmOWsqcsnrzGR+ieaTMVyGyffPVA7F8cXAGt/UxefYv6uSHZLkAFChN5M5Iy1+wjE+xJuPt22H39A==", + "dev": true, + "dependencies": { + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-module-transforms": "^7.19.0", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-validator-identifier": "^7.18.6", + "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.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz", + "integrity": "sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==", + "dev": true, + "dependencies": { + "@babel/helper-module-transforms": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.19.0.tgz", + "integrity": "sha512-HDSuqOQzkU//kfGdiHBt71/hkDTApw4U/cMVgKgX7PqfB3LOaK+2GtCEsBu1dL9CkswDm0Gwehht1dCr421ULQ==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.19.0", + "@babel/helper-plugin-utils": "^7.19.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/plugin-transform-new-target": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz", + "integrity": "sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-object-super": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz", + "integrity": "sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-replace-supers": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-parameters": { + "version": "7.18.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.18.8.tgz", + "integrity": "sha512-ivfbE3X2Ss+Fj8nnXvKJS6sjRG4gzwPMsP+taZC+ZzEGjAYlvENixmt1sZ5Ca6tWls+BlKSGKPJ6OOXvXCbkFg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-property-literals": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz", + "integrity": "sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-regenerator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.6.tgz", + "integrity": "sha512-poqRI2+qiSdeldcz4wTSTXBRryoq3Gc70ye7m7UD5Ww0nE29IXqMl6r7Nd15WBgRd74vloEMlShtH6CKxVzfmQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6", + "regenerator-transform": "^0.15.0" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-reserved-words": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz", + "integrity": "sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-runtime": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.18.10.tgz", + "integrity": "sha512-q5mMeYAdfEbpBAgzl7tBre/la3LeCxmDO1+wMXRdPWbcoMjR3GiXlCLk7JBZVVye0bqTGNMbt0yYVXX1B1jEWQ==", + "dev": true, + "dependencies": { + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.9", + "babel-plugin-polyfill-corejs2": "^0.3.2", + "babel-plugin-polyfill-corejs3": "^0.5.3", + "babel-plugin-polyfill-regenerator": "^0.4.0", + "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.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/plugin-transform-shorthand-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz", + "integrity": "sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-spread": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.19.0.tgz", + "integrity": "sha512-RsuMk7j6n+r752EtzyScnWkQyuJdli6LdO5Klv8Yx0OfPVTcQkIUfS8clx5e9yHXzlnhOZF3CbQ8C2uP5j074w==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-sticky-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz", + "integrity": "sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-template-literals": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz", + "integrity": "sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-typeof-symbol": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz", + "integrity": "sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-escapes": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz", + "integrity": "sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.18.9" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-unicode-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz", + "integrity": "sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==", + "dev": true, + "dependencies": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/preset-env": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.18.10.tgz", + "integrity": "sha512-wVxs1yjFdW3Z/XkNfXKoblxoHgbtUF7/l3PvvP4m02Qz9TZ6uZGxRVYjSQeR87oQmHco9zWitW5J82DJ7sCjvA==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.18.8", + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-validator-option": "^7.18.6", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-async-generator-functions": "^7.18.10", + "@babel/plugin-proposal-class-properties": "^7.18.6", + "@babel/plugin-proposal-class-static-block": "^7.18.6", + "@babel/plugin-proposal-dynamic-import": "^7.18.6", + "@babel/plugin-proposal-export-namespace-from": "^7.18.9", + "@babel/plugin-proposal-json-strings": "^7.18.6", + "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", + "@babel/plugin-proposal-numeric-separator": "^7.18.6", + "@babel/plugin-proposal-object-rest-spread": "^7.18.9", + "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", + "@babel/plugin-proposal-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-private-methods": "^7.18.6", + "@babel/plugin-proposal-private-property-in-object": "^7.18.6", + "@babel/plugin-proposal-unicode-property-regex": "^7.18.6", + "@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-import-assertions": "^7.18.6", + "@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.18.6", + "@babel/plugin-transform-async-to-generator": "^7.18.6", + "@babel/plugin-transform-block-scoped-functions": "^7.18.6", + "@babel/plugin-transform-block-scoping": "^7.18.9", + "@babel/plugin-transform-classes": "^7.18.9", + "@babel/plugin-transform-computed-properties": "^7.18.9", + "@babel/plugin-transform-destructuring": "^7.18.9", + "@babel/plugin-transform-dotall-regex": "^7.18.6", + "@babel/plugin-transform-duplicate-keys": "^7.18.9", + "@babel/plugin-transform-exponentiation-operator": "^7.18.6", + "@babel/plugin-transform-for-of": "^7.18.8", + "@babel/plugin-transform-function-name": "^7.18.9", + "@babel/plugin-transform-literals": "^7.18.9", + "@babel/plugin-transform-member-expression-literals": "^7.18.6", + "@babel/plugin-transform-modules-amd": "^7.18.6", + "@babel/plugin-transform-modules-commonjs": "^7.18.6", + "@babel/plugin-transform-modules-systemjs": "^7.18.9", + "@babel/plugin-transform-modules-umd": "^7.18.6", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.18.6", + "@babel/plugin-transform-new-target": "^7.18.6", + "@babel/plugin-transform-object-super": "^7.18.6", + "@babel/plugin-transform-parameters": "^7.18.8", + "@babel/plugin-transform-property-literals": "^7.18.6", + "@babel/plugin-transform-regenerator": "^7.18.6", + "@babel/plugin-transform-reserved-words": "^7.18.6", + "@babel/plugin-transform-shorthand-properties": "^7.18.6", + "@babel/plugin-transform-spread": "^7.18.9", + "@babel/plugin-transform-sticky-regex": "^7.18.6", + "@babel/plugin-transform-template-literals": "^7.18.9", + "@babel/plugin-transform-typeof-symbol": "^7.18.9", + "@babel/plugin-transform-unicode-escapes": "^7.18.10", + "@babel/plugin-transform-unicode-regex": "^7.18.6", + "@babel/preset-modules": "^0.1.5", + "@babel/types": "^7.18.10", + "babel-plugin-polyfill-corejs2": "^0.3.2", + "babel-plugin-polyfill-corejs3": "^0.5.3", + "babel-plugin-polyfill-regenerator": "^0.4.0", + "core-js-compat": "^3.22.1", + "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.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "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.18.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz", + "integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==", + "dev": true, + "dependencies": { + "regenerator-runtime": "^0.13.4" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", + "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.18.10", + "@babel/types": "^7.18.10" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.19.0.tgz", + "integrity": "sha512-4pKpFRDh+utd2mbRC8JLnlsMUii3PMHjpL6a0SZ4NMZy7YFP9aXORxEhdMVOc9CpWtDF09IkciQLEhK7Ml7gRA==", + "dependencies": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.19.0", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.19.0", + "@babel/types": "^7.19.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/@babel/generator": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.19.0.tgz", + "integrity": "sha512-S1ahxf1gZ2dpoiFgA+ohK9DIpz50bJ0CWs7Zlzb54Z4sG8qmdIrGrVqmy1sAtTVRb+9CU6U8VqT9L0Zj7hxHVg==", + "dependencies": { + "@babel/types": "^7.19.0", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/types": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.19.0.tgz", + "integrity": "sha512-YuGopBq3ke25BVSiS6fgF49Ul9gH1x70Bcr6bqRLjWCkcX8Hre1/5+z+IiWOIerRMSSEfGZVB9z9kyq7wVs9YA==", + "dependencies": { + "@babel/helper-string-parser": "^7.18.10", + "@babel/helper-validator-identifier": "^7.18.6", + "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/@chevrotain/cst-dts-gen": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-10.3.0.tgz", + "integrity": "sha512-7DJPfCtfK1SU1/F3ZmmVv5IsTTcP/iiKFqU1m0H4VzJNvG3/GNxJFQjy4t/veWTAFSPWSj1WCNyq+sc5XKq9yA==", + "dev": true, + "dependencies": { + "@chevrotain/gast": "10.3.0", + "@chevrotain/types": "10.3.0", + "lodash": "4.17.21" + } + }, + "node_modules/@chevrotain/gast": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-10.3.0.tgz", + "integrity": "sha512-kLbGubyLprlyFZ1/c5pkpciCi6WTcRcnKhkfflSdKsZuoy0OmVTEXzrmFCQWzJ+QtmQNtPZTKwIBXJ6Zixp6nA==", + "dev": true, + "dependencies": { + "@chevrotain/types": "10.3.0", + "lodash": "4.17.21" + } + }, + "node_modules/@chevrotain/types": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-10.3.0.tgz", + "integrity": "sha512-LGesL4c5+FyweDsmFukcxmsowpagj1iC4iqkQSIDG3Y7krV2rIOmCDDq4kZff51Asr6yQHEJsWTyvGVHeWQLkw==", + "dev": true + }, + "node_modules/@chevrotain/utils": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-10.3.0.tgz", + "integrity": "sha512-KCpFcX+kNyKlVZQW60ZUGRW5Nsg5u0F2CIgHiDQyg282ouHS9xap2ZEKqhwGE/0nYP46nAPnGPdb/IYh7ZHOsA==", + "dev": true + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@csstools/postcss-cascade-layers": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-1.0.6.tgz", + "integrity": "sha512-ei4Vh4AJwTCXTNj7uzwduoZDO7nLPksQ0TI7OzUlyFq4P4Uhu6hU7R4AlLimDP/s6D3PQdHmRL4f7UOy370UHA==", + "dev": true, + "dependencies": { + "@csstools/selector-specificity": "^2.0.2", + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-color-function": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-1.1.1.tgz", + "integrity": "sha512-Bc0f62WmHdtRDjf5f3e2STwRAl89N2CLb+9iAwzrv4L2hncrbDwnQD9PCq0gtAt7pOI2leIV08HIBUd4jxD8cw==", + "dev": true, + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-font-format-keywords": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-font-format-keywords/-/postcss-font-format-keywords-1.0.1.tgz", + "integrity": "sha512-ZgrlzuUAjXIOc2JueK0X5sZDjCtgimVp/O5CEqTcs5ShWBa6smhWYbS0x5cVc/+rycTDbjjzoP0KTDnUneZGOg==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-hwb-function": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-1.0.2.tgz", + "integrity": "sha512-YHdEru4o3Rsbjmu6vHy4UKOXZD+Rn2zmkAmLRfPet6+Jz4Ojw8cbWxe1n42VaXQhD3CQUXXTooIy8OkVbUcL+w==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-ic-unit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-1.0.1.tgz", + "integrity": "sha512-Ot1rcwRAaRHNKC9tAqoqNZhjdYBzKk1POgWfhN4uCOE47ebGcLRqXjKkApVDpjifL6u2/55ekkpnFcp+s/OZUw==", + "dev": true, + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-is-pseudo-class": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-2.0.7.tgz", + "integrity": "sha512-7JPeVVZHd+jxYdULl87lvjgvWldYu+Bc62s9vD/ED6/QTGjy0jy0US/f6BG53sVMTBJ1lzKZFpYmofBN9eaRiA==", + "dev": true, + "dependencies": { + "@csstools/selector-specificity": "^2.0.0", + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-nested-calc": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-nested-calc/-/postcss-nested-calc-1.0.0.tgz", + "integrity": "sha512-JCsQsw1wjYwv1bJmgjKSoZNvf7R6+wuHDAbi5f/7MbFhl2d/+v+TvBTU4BJH3G1X1H87dHl0mh6TfYogbT/dJQ==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-normalize-display-values": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-normalize-display-values/-/postcss-normalize-display-values-1.0.1.tgz", + "integrity": "sha512-jcOanIbv55OFKQ3sYeFD/T0Ti7AMXc9nM1hZWu8m/2722gOTxFg7xYu4RDLJLeZmPUVQlGzo4jhzvTUq3x4ZUw==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-oklab-function": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-1.1.1.tgz", + "integrity": "sha512-nJpJgsdA3dA9y5pgyb/UfEzE7W5Ka7u0CX0/HIMVBNWzWemdcTH3XwANECU6anWv/ao4vVNLTMxhiPNZsTK6iA==", + "dev": true, + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-progressive-custom-properties": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-1.3.0.tgz", + "integrity": "sha512-ASA9W1aIy5ygskZYuWams4BzafD12ULvSypmaLJT2jvQ8G0M3I8PRQhC0h7mG0Z3LI05+agZjqSR9+K9yaQQjA==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.3" + } + }, + "node_modules/@csstools/postcss-stepped-value-functions": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-1.0.1.tgz", + "integrity": "sha512-dz0LNoo3ijpTOQqEJLY8nyaapl6umbmDcgj4AD0lgVQ572b2eqA1iGZYTTWhrcrHztWDDRAX2DGYyw2VBjvCvQ==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-text-decoration-shorthand": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-1.0.0.tgz", + "integrity": "sha512-c1XwKJ2eMIWrzQenN0XbcfzckOLLJiczqy+YvfGmzoVXd7pT9FfObiSEfzs84bpE/VqfpEuAZ9tCRbZkZxxbdw==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-trigonometric-functions": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-1.0.2.tgz", + "integrity": "sha512-woKaLO///4bb+zZC2s80l+7cm07M7268MsyG3M0ActXXEFi6SuhvriQYcb58iiKGbjwwIU7n45iRLEHypB47Og==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/postcss-unset-value": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-1.0.2.tgz", + "integrity": "sha512-c8J4roPBILnelAsdLr4XOAR/GsTm0GJi4XpcfvoWk3U6KiTCqiFYc63KhRMQQX35jYMp4Ao8Ij9+IZRgMfJp1g==", + "dev": true, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/@csstools/selector-specificity": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.0.2.tgz", + "integrity": "sha512-IkpVW/ehM1hWKln4fCA3NzJU8KwD+kIOvPZA4cqxoJHtE21CCzjyp+Kxbu0i5I4tBNOlXPL9mjwnWlL0VEG4Fg==", + "dev": true, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2", + "postcss-selector-parser": "^6.0.10" + } + }, + "node_modules/@cypress/request": { + "version": "2.88.10", + "resolved": "https://registry.npmjs.org/@cypress/request/-/request-2.88.10.tgz", + "integrity": "sha512-Zp7F+R93N0yZyG34GutyTNr+okam7s/Fzc1+i3kcqOP8vk6OuajuE9qZJ6Rs+10/1JFtXFYMdyarnU1rZuJesg==", + "dev": true, + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "http-signature": "~1.3.6", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@cypress/request/node_modules/qs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/@cypress/xvfb": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.2.4.tgz", + "integrity": "sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==", + "dev": true, + "dependencies": { + "debug": "^3.1.0", + "lodash.once": "^4.1.1" + } + }, + "node_modules/@cypress/xvfb/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/@dabh/diagnostics": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "dev": true, + "dependencies": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, + "node_modules/@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.5.tgz", + "integrity": "sha512-UHkDFCfSGTuXq08oQltXxSZmH1TXyWsL+4QhZDWvvLl6mEJQqk3u7/wq1LjhrrAXYIllaTtRSzUXl4Olkf2J8A==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", + "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz", + "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.5.2", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "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/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/@eslint/eslintrc/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/@eslint/eslintrc/node_modules/globals": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/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==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "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/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/@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/@eslint/js": { + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.42.0.tgz", + "integrity": "sha512-6SWlXpWU5AvId8Ac7zjzmIOqMOba/JWY8XZ4A7q7Gn1Vlfg/SFFIlrtHXt9nPn4op9ZPAkl91Jao+QQv3r/ukw==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/@faker-js/faker": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-5.5.3.tgz", + "integrity": "sha512-R11tGE6yIFwqpaIqcfkcg7AICXzFg14+5h5v0TfF/9+RMDL6jhzCy/pxHVOfbALGdtVYdt6JdR21tuxEgl34dw==", + "dev": true + }, + "node_modules/@fortawesome/angular-fontawesome": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/@fortawesome/angular-fontawesome/-/angular-fontawesome-0.11.1.tgz", + "integrity": "sha512-Ngzm5MVxk76ZhYpPTNOI/mpYNz9bzwfBXC5L9mktLgOONjBuYBPVt+bH8lny8hNtDk0ppZzXsMN6CO7hckdfnw==", + "dependencies": { + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@angular/core": "^14.0.0", + "@fortawesome/fontawesome-svg-core": "~1.2.27 || ~1.3.0-beta2 || ^6.1.0" + } + }, + "node_modules/@fortawesome/fontawesome-common-types": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.2.0.tgz", + "integrity": "sha512-rBevIsj2nclStJ7AxTdfsa3ovHb1H+qApwrxcTVo+NNdeJiB9V75hsKfrkG5AwNcRUNxrPPiScGYCNmLMoh8pg==", + "hasInstallScript": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/fontawesome-svg-core": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.4.0.tgz", + "integrity": "sha512-Bertv8xOiVELz5raB2FlXDPKt+m94MQ3JgDfsVbrqNpLU9+UE2E18GKjLKw+d3XbeYPqg1pzyQKGsrzbw+pPaw==", + "hasInstallScript": true, + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.4.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/fontawesome-svg-core/node_modules/@fortawesome/fontawesome-common-types": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.4.0.tgz", + "integrity": "sha512-HNii132xfomg5QVZw0HwXXpN22s7VBHQBv9CeOu9tfJnhsWQNd2lmTNi8CSrnw5B+5YOmzu1UoPAyxaXsJ6RgQ==", + "hasInstallScript": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/@fortawesome/free-solid-svg-icons": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.2.0.tgz", + "integrity": "sha512-UjCILHIQ4I8cN46EiQn0CZL/h8AwCGgR//1c4R96Q5viSRwuKVo0NdQEc4bm+69ZwC0dUvjbDqAHF1RR5FA3XA==", + "hasInstallScript": true, + "dependencies": { + "@fortawesome/fontawesome-common-types": "6.2.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@gar/promisify": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", + "dev": true + }, + "node_modules/@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==", + "dev": true + }, + "node_modules/@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "dev": true, + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", + "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/config-array/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/@humanwhocodes/config-array/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/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "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/@isaacs/string-locale-compare": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@isaacs/string-locale-compare/-/string-locale-compare-1.1.0.tgz", + "integrity": "sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ==", + "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/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": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-28.1.3.tgz", + "integrity": "sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw==", + "dev": true, + "dependencies": { + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^28.1.3", + "jest-util": "^28.1.3", + "slash": "^3.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/console/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/console/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "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/@jest/console/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/console/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==", + "dev": true + }, + "node_modules/@jest/console/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/core": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-28.1.3.tgz", + "integrity": "sha512-CIKBrlaKOzA7YG19BEqCw3SLIsEwjZkeJzf5bdooVnW4bH5cktqe3JX+G2YV1aK5vP8N9na1IGWFzYaTp6k6NA==", + "dev": true, + "dependencies": { + "@jest/console": "^28.1.3", + "@jest/reporters": "^28.1.3", + "@jest/test-result": "^28.1.3", + "@jest/transform": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^28.1.3", + "jest-config": "^28.1.3", + "jest-haste-map": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-regex-util": "^28.0.2", + "jest-resolve": "^28.1.3", + "jest-resolve-dependencies": "^28.1.3", + "jest-runner": "^28.1.3", + "jest-runtime": "^28.1.3", + "jest-snapshot": "^28.1.3", + "jest-util": "^28.1.3", + "jest-validate": "^28.1.3", + "jest-watcher": "^28.1.3", + "micromatch": "^4.0.4", + "pretty-format": "^28.1.3", + "rimraf": "^3.0.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/core/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/core/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "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/@jest/core/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/core/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==", + "dev": true + }, + "node_modules/@jest/core/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/core/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/environment": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-28.1.3.tgz", + "integrity": "sha512-1bf40cMFTEkKyEf585R9Iz1WayDjHoHqvts0XFYEqyKM3cFWDpeMoqKKTAF9LSYQModPUlh8FKptoM2YcMWAXA==", + "dev": true, + "dependencies": { + "@jest/fake-timers": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "jest-mock": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-28.1.3.tgz", + "integrity": "sha512-lzc8CpUbSoE4dqT0U+g1qODQjBRHPpCPXissXD4mS9+sWQdmmpeJ9zSH1rS1HEkrsMN0fb7nKrJ9giAR1d3wBw==", + "dev": true, + "dependencies": { + "expect": "^28.1.3", + "jest-snapshot": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/expect-utils": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-28.1.3.tgz", + "integrity": "sha512-wvbi9LUrHJLn3NlDW6wF2hvIMtd4JUl2QNVrjq+IBSHirgfrR3o9RnVtxzdEGO2n9JyIWwHnLfby5KzqBGg2YA==", + "dev": true, + "dependencies": { + "jest-get-type": "^28.0.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-28.1.3.tgz", + "integrity": "sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw==", + "dev": true, + "dependencies": { + "@jest/types": "^28.1.3", + "@sinonjs/fake-timers": "^9.1.2", + "@types/node": "*", + "jest-message-util": "^28.1.3", + "jest-mock": "^28.1.3", + "jest-util": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/globals": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-28.1.3.tgz", + "integrity": "sha512-XFU4P4phyryCXu1pbcqMO0GSQcYe1IsalYCDzRNyhetyeyxMcIxa11qPNDpVNLeretItNqEmYYQn1UYz/5x1NA==", + "dev": true, + "dependencies": { + "@jest/environment": "^28.1.3", + "@jest/expect": "^28.1.3", + "@jest/types": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/reporters": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-28.1.3.tgz", + "integrity": "sha512-JuAy7wkxQZVNU/V6g9xKzCGC5LVXx9FDcABKsSXp5MiKPEE2144a/vXTEDoyzjUpZKfVwp08Wqg5A4WfTMAzjg==", + "dev": true, + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^28.1.3", + "@jest/test-result": "^28.1.3", + "@jest/transform": "^28.1.3", + "@jest/types": "^28.1.3", + "@jridgewell/trace-mapping": "^0.3.13", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "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-message-util": "^28.1.3", + "jest-util": "^28.1.3", + "jest-worker": "^28.1.3", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.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/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/reporters/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/@jest/reporters/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "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/@jest/reporters/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/reporters/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==", + "dev": true + }, + "node_modules/@jest/reporters/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@jest/reporters/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/reporters/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/@jest/reporters/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/schemas": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", + "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", + "dev": true, + "dependencies": { + "@sinclair/typebox": "^0.24.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "28.1.2", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-28.1.2.tgz", + "integrity": "sha512-cV8Lx3BeStJb8ipPHnqVw/IM2VCMWO3crWZzYodSIkxXnRcXJipCdx1JCK0K5MsJJouZQTH73mzf4vgxRaH9ww==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.13", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/test-result": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-28.1.3.tgz", + "integrity": "sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg==", + "dev": true, + "dependencies": { + "@jest/console": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/test-sequencer": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-28.1.3.tgz", + "integrity": "sha512-NIMPEqqa59MWnDi1kvXXpYbqsfQmSJsIbnd85mdVGkiDfQ9WQQTXOLsvISUfonmnBT+w85WEgneCigEEdHDFxw==", + "dev": true, + "dependencies": { + "@jest/test-result": "^28.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^28.1.3", + "slash": "^3.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-28.1.3.tgz", + "integrity": "sha512-u5dT5di+oFI6hfcLOHGTAfmUxFRrjK+vnaP0kkVow9Md/M7V/MxqQMOz/VV25UZO8pzeA9PjfTpOu6BDuwSPQA==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^28.1.3", + "@jridgewell/trace-mapping": "^0.3.13", + "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": "^28.1.3", + "jest-regex-util": "^28.0.2", + "jest-util": "^28.1.3", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/transform/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/transform/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "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/@jest/transform/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/transform/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==", + "dev": true + }, + "node_modules/@jest/transform/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/transform/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/types": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", + "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", + "dev": true, + "dependencies": { + "@jest/schemas": "^28.1.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/types/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@jest/types/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "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/@jest/types/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@jest/types/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==", + "dev": true + }, + "node_modules/@jest/types/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/types/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "dependencies": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", + "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "node_modules/@jridgewell/source-map/node_modules/@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", + "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", + "dependencies": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + } + }, + "node_modules/@kwsites/file-exists": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", + "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1" + } + }, + "node_modules/@kwsites/promise-deferred": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz", + "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==", + "dev": true + }, + "node_modules/@leichtgewicht/ip-codec": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", + "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==", + "dev": true + }, + "node_modules/@mattlewis92/dom-autoscroller": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@mattlewis92/dom-autoscroller/-/dom-autoscroller-2.4.2.tgz", + "integrity": "sha512-YbrUWREPGEjE/FU6foXcAT1YbVwqD/jkYnY1dFb0o4AxtP3s4xKBthlELjndZih8uwsDWgQZx1eNskRNe2BgZQ==" + }, + "node_modules/@ng-bootstrap/ng-bootstrap": { + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-13.1.1.tgz", + "integrity": "sha512-R6qnmFKT2EwwijBHw7rUXqyo5W90OImHOv7BlsxMNnZLIksWIhqwU00k4UBTfRTnd6JsTPuj/co3MaP61ajILA==", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/common": "^14.1.0", + "@angular/core": "^14.1.0", + "@angular/forms": "^14.1.0", + "@angular/localize": "^14.1.0", + "@popperjs/core": "^2.10.2", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@ngtools/webpack": { + "version": "14.2.1", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-14.2.1.tgz", + "integrity": "sha512-9tsfx2ZQscnpszVkdoJIIxZZDwitgmbPNrsQmyfHhwqPeJ8UUnAk6RNTfHjtX31VCmIM+qdGJsX7vDR9ye3/uA==", + "dev": true, + "engines": { + "node": "^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "@angular/compiler-cli": "^14.0.0", + "typescript": ">=4.6.2 <4.9", + "webpack": "^5.54.0" + } + }, + "node_modules/@ngx-translate/core": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@ngx-translate/core/-/core-14.0.0.tgz", + "integrity": "sha512-UevdwNCXMRCdJv//0kC8h2eSfmi02r29xeE8E9gJ1Al4D4jEJ7eiLPdjslTMc21oJNGguqqWeEVjf64SFtvw2w==", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/core": ">=13.0.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/@ngx-translate/http-loader": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@ngx-translate/http-loader/-/http-loader-7.0.0.tgz", + "integrity": "sha512-j+NpXXlcGVdyUNyY/qsJrqqeAdJdizCd+GKh3usXExSqy1aE9866jlAIL+xrfDU4w+LiMoma5pgE4emvFebZmA==", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/common": ">=13.0.0", + "@ngx-translate/core": ">=14.0.0", + "rxjs": "^6.5.3 || ^7.4.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/arborist": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-4.3.1.tgz", + "integrity": "sha512-yMRgZVDpwWjplorzt9SFSaakWx6QIK248Nw4ZFgkrAy/GvJaFRaSZzE6nD7JBK5r8g/+PTxFq5Wj/sfciE7x+A==", + "dev": true, + "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/installed-package-contents": "^1.0.7", + "@npmcli/map-workspaces": "^2.0.0", + "@npmcli/metavuln-calculator": "^2.0.0", + "@npmcli/move-file": "^1.1.0", + "@npmcli/name-from-folder": "^1.0.1", + "@npmcli/node-gyp": "^1.0.3", + "@npmcli/package-json": "^1.0.1", + "@npmcli/run-script": "^2.0.0", + "bin-links": "^3.0.0", + "cacache": "^15.0.3", + "common-ancestor-path": "^1.0.1", + "json-parse-even-better-errors": "^2.3.1", + "json-stringify-nice": "^1.1.4", + "mkdirp": "^1.0.4", + "mkdirp-infer-owner": "^2.0.0", + "npm-install-checks": "^4.0.0", + "npm-package-arg": "^8.1.5", + "npm-pick-manifest": "^6.1.0", + "npm-registry-fetch": "^12.0.1", + "pacote": "^12.0.2", + "parse-conflict-json": "^2.0.1", + "proc-log": "^1.0.0", + "promise-all-reject-late": "^1.0.0", + "promise-call-limit": "^1.0.1", + "read-package-json-fast": "^2.0.2", + "readdir-scoped-modules": "^1.1.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "ssri": "^8.0.1", + "treeverse": "^1.0.4", + "walk-up-path": "^1.0.0" + }, + "bin": { + "arborist": "bin/index.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16" + } + }, + "node_modules/@npmcli/arborist/node_modules/@npmcli/fs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", + "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", + "dev": true, + "dependencies": { + "@gar/promisify": "^1.0.1", + "semver": "^7.3.5" + } + }, + "node_modules/@npmcli/arborist/node_modules/@npmcli/git": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-2.1.0.tgz", + "integrity": "sha512-/hBFX/QG1b+N7PZBFs0bi+evgRZcK9nWBxQKZkGoXUT5hJSwl5c4d7y8/hm+NQZRPhQ67RzFaj5UM9YeyKoryw==", + "dev": true, + "dependencies": { + "@npmcli/promise-spawn": "^1.3.2", + "lru-cache": "^6.0.0", + "mkdirp": "^1.0.4", + "npm-pick-manifest": "^6.1.1", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^2.0.2" + } + }, + "node_modules/@npmcli/arborist/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/@npmcli/arborist/node_modules/@npmcli/node-gyp": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-1.0.3.tgz", + "integrity": "sha512-fnkhw+fmX65kiLqk6E3BFLXNC26rUhK90zVwe2yncPliVT/Qos3xjhTLE59Df8KnPlcwIERXKVlU1bXoUQ+liA==", + "dev": true + }, + "node_modules/@npmcli/arborist/node_modules/@npmcli/promise-spawn": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-1.3.2.tgz", + "integrity": "sha512-QyAGYo/Fbj4MXeGdJcFzZ+FkDkomfRBrPM+9QYJSg+PxgAUL+LU3FneQk37rKR2/zjqkCV1BLHccX98wRXG3Sg==", + "dev": true, + "dependencies": { + "infer-owner": "^1.0.4" + } + }, + "node_modules/@npmcli/arborist/node_modules/@npmcli/run-script": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-2.0.0.tgz", + "integrity": "sha512-fSan/Pu11xS/TdaTpTB0MRn9guwGU8dye+x56mEVgBEd/QsybBbYcAL0phPXi8SGWFEChkQd6M9qL4y6VOpFig==", + "dev": true, + "dependencies": { + "@npmcli/node-gyp": "^1.0.2", + "@npmcli/promise-spawn": "^1.3.2", + "node-gyp": "^8.2.0", + "read-package-json-fast": "^2.0.1" + } + }, + "node_modules/@npmcli/arborist/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/@npmcli/arborist/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/@npmcli/arborist/node_modules/builtins": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz", + "integrity": "sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==", + "dev": true + }, + "node_modules/@npmcli/arborist/node_modules/cacache": { + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", + "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", + "dev": true, + "dependencies": { + "@npmcli/fs": "^1.0.0", + "@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/@npmcli/arborist/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@npmcli/arborist/node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@npmcli/arborist/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/@npmcli/arborist/node_modules/ignore-walk": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-4.0.1.tgz", + "integrity": "sha512-rzDQLaW4jQbh2YrOFlJdCtX8qgJTehFRYiUB2r1osqTeDzV/3+Jh8fz1oAPzUThf3iku8Ds4IDqawI5d8mUiQw==", + "dev": true, + "dependencies": { + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@npmcli/arborist/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==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@npmcli/arborist/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/@npmcli/arborist/node_modules/minipass-fetch": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", + "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", + "dev": true, + "dependencies": { + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "optionalDependencies": { + "encoding": "^0.1.12" + } + }, + "node_modules/@npmcli/arborist/node_modules/node-gyp": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", + "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", + "dev": true, + "dependencies": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^9.1.0", + "nopt": "^5.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": ">= 10.12.0" + } + }, + "node_modules/@npmcli/arborist/node_modules/node-gyp/node_modules/make-fetch-happen": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", + "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", + "dev": true, + "dependencies": { + "agentkeepalive": "^4.1.3", + "cacache": "^15.2.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.2", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^6.0.0", + "ssri": "^8.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@npmcli/arborist/node_modules/npm-install-checks": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-4.0.0.tgz", + "integrity": "sha512-09OmyDkNLYwqKPOnbI8exiOZU2GVVmQp7tgez2BPi5OZC8M82elDAps7sxC4l//uSUtotWqoEIDwjRvWH4qz8w==", + "dev": true, + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@npmcli/arborist/node_modules/npm-package-arg": { + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-8.1.5.tgz", + "integrity": "sha512-LhgZrg0n0VgvzVdSm1oiZworPbTxYHUJCgtsJW8mGvlDpxTM1vSJc3m5QZeUkhAHIzbz3VCHd/R4osi1L1Tg/Q==", + "dev": true, + "dependencies": { + "hosted-git-info": "^4.0.1", + "semver": "^7.3.4", + "validate-npm-package-name": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@npmcli/arborist/node_modules/npm-packlist": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-3.0.0.tgz", + "integrity": "sha512-L/cbzmutAwII5glUcf2DBRNY/d0TFd4e/FnaZigJV6JD85RHZXJFGwCndjMWiiViiWSsWt3tiOLpI3ByTnIdFQ==", + "dev": true, + "dependencies": { + "glob": "^7.1.6", + "ignore-walk": "^4.0.1", + "npm-bundled": "^1.1.1", + "npm-normalize-package-bin": "^1.0.1" + }, + "bin": { + "npm-packlist": "bin/index.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@npmcli/arborist/node_modules/npm-pick-manifest": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-6.1.1.tgz", + "integrity": "sha512-dBsdBtORT84S8V8UTad1WlUyKIY9iMsAmqxHbLdeEeBNMLQDlDWWra3wYUx9EBEIiG/YwAy0XyNHDd2goAsfuA==", + "dev": true, + "dependencies": { + "npm-install-checks": "^4.0.0", + "npm-normalize-package-bin": "^1.0.1", + "npm-package-arg": "^8.1.2", + "semver": "^7.3.4" + } + }, + "node_modules/@npmcli/arborist/node_modules/npm-registry-fetch": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-12.0.2.tgz", + "integrity": "sha512-Df5QT3RaJnXYuOwtXBXS9BWs+tHH2olvkCLh6jcR/b/u3DvPMlp3J0TvvYwplPKxHMOwfg287PYih9QqaVFoKA==", + "dev": true, + "dependencies": { + "make-fetch-happen": "^10.0.1", + "minipass": "^3.1.6", + "minipass-fetch": "^1.4.1", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.1.2", + "npm-package-arg": "^8.1.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16" + } + }, + "node_modules/@npmcli/arborist/node_modules/pacote": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-12.0.3.tgz", + "integrity": "sha512-CdYEl03JDrRO3x18uHjBYA9TyoW8gy+ThVcypcDkxPtKlw76e4ejhYB6i9lJ+/cebbjpqPW/CijjqxwDTts8Ow==", + "dev": true, + "dependencies": { + "@npmcli/git": "^2.1.0", + "@npmcli/installed-package-contents": "^1.0.6", + "@npmcli/promise-spawn": "^1.2.0", + "@npmcli/run-script": "^2.0.0", + "cacache": "^15.0.5", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "infer-owner": "^1.0.4", + "minipass": "^3.1.3", + "mkdirp": "^1.0.3", + "npm-package-arg": "^8.0.1", + "npm-packlist": "^3.0.0", + "npm-pick-manifest": "^6.0.0", + "npm-registry-fetch": "^12.0.0", + "promise-retry": "^2.0.1", + "read-package-json-fast": "^2.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.1.0" + }, + "bin": { + "pacote": "lib/bin.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16" + } + }, + "node_modules/@npmcli/arborist/node_modules/proc-log": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-1.0.0.tgz", + "integrity": "sha512-aCk8AO51s+4JyuYGg3Q/a6gnrlDO09NpVWePtjp7xwphcoQ04x5WAfCyugcsbLooWcMJ87CLkD4+604IckEdhg==", + "dev": true + }, + "node_modules/@npmcli/arborist/node_modules/socks-proxy-agent": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz", + "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==", + "dev": true, + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@npmcli/arborist/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/@npmcli/arborist/node_modules/validate-npm-package-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz", + "integrity": "sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw==", + "dev": true, + "dependencies": { + "builtins": "^1.0.3" + } + }, + "node_modules/@npmcli/fs": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.2.tgz", + "integrity": "sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==", + "dev": true, + "dependencies": { + "@gar/promisify": "^1.1.3", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/git": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-3.0.2.tgz", + "integrity": "sha512-CAcd08y3DWBJqJDpfuVL0uijlq5oaXaOJEKHKc4wqrjd00gkvTZB+nFuLn+doOOKddaQS9JfqtNoFCO2LCvA3w==", + "dev": true, + "dependencies": { + "@npmcli/promise-spawn": "^3.0.0", + "lru-cache": "^7.4.4", + "mkdirp": "^1.0.4", + "npm-pick-manifest": "^7.0.0", + "proc-log": "^2.0.0", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^2.0.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/installed-package-contents": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-1.0.7.tgz", + "integrity": "sha512-9rufe0wnJusCQoLpV9ZPKIVP55itrM5BxOXs10DmdbRfgWtHy1LDyskbwRnBghuB0PrF7pNPOqREVtpz4HqzKw==", + "dev": true, + "dependencies": { + "npm-bundled": "^1.1.1", + "npm-normalize-package-bin": "^1.0.1" + }, + "bin": { + "installed-package-contents": "index.js" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@npmcli/map-workspaces": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@npmcli/map-workspaces/-/map-workspaces-2.0.4.tgz", + "integrity": "sha512-bMo0aAfwhVwqoVM5UzX1DJnlvVvzDCHae821jv48L1EsrYwfOZChlqWYXEtto/+BkBXetPbEWgau++/brh4oVg==", + "dev": true, + "dependencies": { + "@npmcli/name-from-folder": "^1.0.1", + "glob": "^8.0.1", + "minimatch": "^5.0.1", + "read-package-json-fast": "^2.0.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/metavuln-calculator": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/metavuln-calculator/-/metavuln-calculator-2.0.0.tgz", + "integrity": "sha512-VVW+JhWCKRwCTE+0xvD6p3uV4WpqocNYYtzyvenqL/u1Q3Xx6fGTJ+6UoIoii07fbuEO9U3IIyuGY0CYHDv1sg==", + "dev": true, + "dependencies": { + "cacache": "^15.0.5", + "json-parse-even-better-errors": "^2.3.1", + "pacote": "^12.0.0", + "semver": "^7.3.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/@npmcli/fs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", + "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", + "dev": true, + "dependencies": { + "@gar/promisify": "^1.0.1", + "semver": "^7.3.5" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/@npmcli/git": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-2.1.0.tgz", + "integrity": "sha512-/hBFX/QG1b+N7PZBFs0bi+evgRZcK9nWBxQKZkGoXUT5hJSwl5c4d7y8/hm+NQZRPhQ67RzFaj5UM9YeyKoryw==", + "dev": true, + "dependencies": { + "@npmcli/promise-spawn": "^1.3.2", + "lru-cache": "^6.0.0", + "mkdirp": "^1.0.4", + "npm-pick-manifest": "^6.1.1", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^2.0.2" + } + }, + "node_modules/@npmcli/metavuln-calculator/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/@npmcli/metavuln-calculator/node_modules/@npmcli/node-gyp": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-1.0.3.tgz", + "integrity": "sha512-fnkhw+fmX65kiLqk6E3BFLXNC26rUhK90zVwe2yncPliVT/Qos3xjhTLE59Df8KnPlcwIERXKVlU1bXoUQ+liA==", + "dev": true + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/@npmcli/promise-spawn": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-1.3.2.tgz", + "integrity": "sha512-QyAGYo/Fbj4MXeGdJcFzZ+FkDkomfRBrPM+9QYJSg+PxgAUL+LU3FneQk37rKR2/zjqkCV1BLHccX98wRXG3Sg==", + "dev": true, + "dependencies": { + "infer-owner": "^1.0.4" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/@npmcli/run-script": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-2.0.0.tgz", + "integrity": "sha512-fSan/Pu11xS/TdaTpTB0MRn9guwGU8dye+x56mEVgBEd/QsybBbYcAL0phPXi8SGWFEChkQd6M9qL4y6VOpFig==", + "dev": true, + "dependencies": { + "@npmcli/node-gyp": "^1.0.2", + "@npmcli/promise-spawn": "^1.3.2", + "node-gyp": "^8.2.0", + "read-package-json-fast": "^2.0.1" + } + }, + "node_modules/@npmcli/metavuln-calculator/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/@npmcli/metavuln-calculator/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/@npmcli/metavuln-calculator/node_modules/builtins": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz", + "integrity": "sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==", + "dev": true + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/cacache": { + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", + "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", + "dev": true, + "dependencies": { + "@npmcli/fs": "^1.0.0", + "@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/@npmcli/metavuln-calculator/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@npmcli/metavuln-calculator/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/@npmcli/metavuln-calculator/node_modules/ignore-walk": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-4.0.1.tgz", + "integrity": "sha512-rzDQLaW4jQbh2YrOFlJdCtX8qgJTehFRYiUB2r1osqTeDzV/3+Jh8fz1oAPzUThf3iku8Ds4IDqawI5d8mUiQw==", + "dev": true, + "dependencies": { + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@npmcli/metavuln-calculator/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==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@npmcli/metavuln-calculator/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/@npmcli/metavuln-calculator/node_modules/minipass-fetch": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", + "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", + "dev": true, + "dependencies": { + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "optionalDependencies": { + "encoding": "^0.1.12" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/node-gyp": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", + "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", + "dev": true, + "dependencies": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^9.1.0", + "nopt": "^5.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": ">= 10.12.0" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/node-gyp/node_modules/make-fetch-happen": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", + "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", + "dev": true, + "dependencies": { + "agentkeepalive": "^4.1.3", + "cacache": "^15.2.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.2", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^6.0.0", + "ssri": "^8.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/npm-install-checks": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-4.0.0.tgz", + "integrity": "sha512-09OmyDkNLYwqKPOnbI8exiOZU2GVVmQp7tgez2BPi5OZC8M82elDAps7sxC4l//uSUtotWqoEIDwjRvWH4qz8w==", + "dev": true, + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/npm-package-arg": { + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-8.1.5.tgz", + "integrity": "sha512-LhgZrg0n0VgvzVdSm1oiZworPbTxYHUJCgtsJW8mGvlDpxTM1vSJc3m5QZeUkhAHIzbz3VCHd/R4osi1L1Tg/Q==", + "dev": true, + "dependencies": { + "hosted-git-info": "^4.0.1", + "semver": "^7.3.4", + "validate-npm-package-name": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/npm-packlist": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-3.0.0.tgz", + "integrity": "sha512-L/cbzmutAwII5glUcf2DBRNY/d0TFd4e/FnaZigJV6JD85RHZXJFGwCndjMWiiViiWSsWt3tiOLpI3ByTnIdFQ==", + "dev": true, + "dependencies": { + "glob": "^7.1.6", + "ignore-walk": "^4.0.1", + "npm-bundled": "^1.1.1", + "npm-normalize-package-bin": "^1.0.1" + }, + "bin": { + "npm-packlist": "bin/index.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/npm-pick-manifest": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-6.1.1.tgz", + "integrity": "sha512-dBsdBtORT84S8V8UTad1WlUyKIY9iMsAmqxHbLdeEeBNMLQDlDWWra3wYUx9EBEIiG/YwAy0XyNHDd2goAsfuA==", + "dev": true, + "dependencies": { + "npm-install-checks": "^4.0.0", + "npm-normalize-package-bin": "^1.0.1", + "npm-package-arg": "^8.1.2", + "semver": "^7.3.4" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/npm-registry-fetch": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-12.0.2.tgz", + "integrity": "sha512-Df5QT3RaJnXYuOwtXBXS9BWs+tHH2olvkCLh6jcR/b/u3DvPMlp3J0TvvYwplPKxHMOwfg287PYih9QqaVFoKA==", + "dev": true, + "dependencies": { + "make-fetch-happen": "^10.0.1", + "minipass": "^3.1.6", + "minipass-fetch": "^1.4.1", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.1.2", + "npm-package-arg": "^8.1.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/pacote": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-12.0.3.tgz", + "integrity": "sha512-CdYEl03JDrRO3x18uHjBYA9TyoW8gy+ThVcypcDkxPtKlw76e4ejhYB6i9lJ+/cebbjpqPW/CijjqxwDTts8Ow==", + "dev": true, + "dependencies": { + "@npmcli/git": "^2.1.0", + "@npmcli/installed-package-contents": "^1.0.6", + "@npmcli/promise-spawn": "^1.2.0", + "@npmcli/run-script": "^2.0.0", + "cacache": "^15.0.5", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "infer-owner": "^1.0.4", + "minipass": "^3.1.3", + "mkdirp": "^1.0.3", + "npm-package-arg": "^8.0.1", + "npm-packlist": "^3.0.0", + "npm-pick-manifest": "^6.0.0", + "npm-registry-fetch": "^12.0.0", + "promise-retry": "^2.0.1", + "read-package-json-fast": "^2.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.1.0" + }, + "bin": { + "pacote": "lib/bin.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16" + } + }, + "node_modules/@npmcli/metavuln-calculator/node_modules/socks-proxy-agent": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz", + "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==", + "dev": true, + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@npmcli/metavuln-calculator/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/@npmcli/metavuln-calculator/node_modules/validate-npm-package-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz", + "integrity": "sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw==", + "dev": true, + "dependencies": { + "builtins": "^1.0.3" + } + }, + "node_modules/@npmcli/move-file": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-2.0.1.tgz", + "integrity": "sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==", + "dev": true, + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/name-from-folder": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/name-from-folder/-/name-from-folder-1.0.1.tgz", + "integrity": "sha512-qq3oEfcLFwNfEYOQ8HLimRGKlD8WSeGEdtUa7hmzpR8Sa7haL1KVQrvgO6wqMjhWFFVjgtrh1gIxDz+P8sjUaA==", + "dev": true + }, + "node_modules/@npmcli/node-gyp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-2.0.0.tgz", + "integrity": "sha512-doNI35wIe3bBaEgrlPfdJPaCpUR89pJWep4Hq3aRdh6gKazIVWfs0jHttvSSoq47ZXgC7h73kDsUl8AoIQUB+A==", + "dev": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/package-json": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/package-json/-/package-json-1.0.1.tgz", + "integrity": "sha512-y6jnu76E9C23osz8gEMBayZmaZ69vFOIk8vR1FJL/wbEJ54+9aVG9rLTjQKSXfgYZEr50nw1txBBFfBZZe+bYg==", + "dev": true, + "dependencies": { + "json-parse-even-better-errors": "^2.3.1" + } + }, + "node_modules/@npmcli/promise-spawn": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-3.0.0.tgz", + "integrity": "sha512-s9SgS+p3a9Eohe68cSI3fi+hpcZUmXq5P7w0kMlAsWVtR7XbK3ptkZqKT2cK1zLDObJ3sR+8P59sJE0w/KTL1g==", + "dev": true, + "dependencies": { + "infer-owner": "^1.0.4" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/run-script": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-4.2.1.tgz", + "integrity": "sha512-7dqywvVudPSrRCW5nTHpHgeWnbBtz8cFkOuKrecm6ih+oO9ciydhWt6OF7HlqupRRmB8Q/gECVdB9LMfToJbRg==", + "dev": true, + "dependencies": { + "@npmcli/node-gyp": "^2.0.0", + "@npmcli/promise-spawn": "^3.0.0", + "node-gyp": "^9.0.0", + "read-package-json-fast": "^2.0.3", + "which": "^2.0.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@octokit/auth-token": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz", + "integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==", + "dev": true, + "dependencies": { + "@octokit/types": "^6.0.3" + } + }, + "node_modules/@octokit/core": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz", + "integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==", + "dev": true, + "dependencies": { + "@octokit/auth-token": "^2.4.4", + "@octokit/graphql": "^4.5.8", + "@octokit/request": "^5.6.3", + "@octokit/request-error": "^2.0.5", + "@octokit/types": "^6.0.3", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/endpoint": { + "version": "6.0.12", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz", + "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==", + "dev": true, + "dependencies": { + "@octokit/types": "^6.0.3", + "is-plain-object": "^5.0.0", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/endpoint/node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@octokit/graphql": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz", + "integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==", + "dev": true, + "dependencies": { + "@octokit/request": "^5.6.0", + "@octokit/types": "^6.0.3", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "12.11.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", + "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==", + "dev": true + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "2.21.3", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.3.tgz", + "integrity": "sha512-aCZTEf0y2h3OLbrgKkrfFdjRL6eSOo8komneVQJnYecAxIej7Bafor2xhuDJOIFau4pk0i/P28/XgtbyPF0ZHw==", + "dev": true, + "dependencies": { + "@octokit/types": "^6.40.0" + }, + "peerDependencies": { + "@octokit/core": ">=2" + } + }, + "node_modules/@octokit/plugin-request-log": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", + "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==", + "dev": true, + "peerDependencies": { + "@octokit/core": ">=3" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "5.16.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz", + "integrity": "sha512-8QFz29Fg5jDuTPXVtey05BLm7OB+M8fnvE64RNegzX7U+5NUXcOcnpTIK0YfSHBg8gYd0oxIq3IZTe9SfPZiRw==", + "dev": true, + "dependencies": { + "@octokit/types": "^6.39.0", + "deprecation": "^2.3.1" + }, + "peerDependencies": { + "@octokit/core": ">=3" + } + }, + "node_modules/@octokit/request": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz", + "integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==", + "dev": true, + "dependencies": { + "@octokit/endpoint": "^6.0.1", + "@octokit/request-error": "^2.1.0", + "@octokit/types": "^6.16.1", + "is-plain-object": "^5.0.0", + "node-fetch": "^2.6.7", + "universal-user-agent": "^6.0.0" + } + }, + "node_modules/@octokit/request-error": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", + "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", + "dev": true, + "dependencies": { + "@octokit/types": "^6.0.3", + "deprecation": "^2.0.0", + "once": "^1.4.0" + } + }, + "node_modules/@octokit/request/node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/@octokit/rest": { + "version": "18.12.0", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.12.0.tgz", + "integrity": "sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q==", + "dev": true, + "dependencies": { + "@octokit/core": "^3.5.1", + "@octokit/plugin-paginate-rest": "^2.16.8", + "@octokit/plugin-request-log": "^1.0.4", + "@octokit/plugin-rest-endpoint-methods": "^5.12.0" + } + }, + "node_modules/@octokit/types": { + "version": "6.41.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", + "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", + "dev": true, + "dependencies": { + "@octokit/openapi-types": "^12.11.0" + } + }, + "node_modules/@pkgr/utils": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.3.1.tgz", + "integrity": "sha512-wfzX8kc1PMyUILA+1Z/EqoE4UCXGy0iRGMhPwdfae1+f0OXlLqCk+By+aMzgJBzR9AzS4CDizioG6Ss1gvAFJw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "is-glob": "^4.0.3", + "open": "^8.4.0", + "picocolors": "^1.0.0", + "tiny-glob": "^0.2.9", + "tslib": "^2.4.0" + }, + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/@polka/url": { + "version": "1.0.0-next.21", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz", + "integrity": "sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==", + "dev": true + }, + "node_modules/@popperjs/core": { + "version": "2.11.6", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz", + "integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@scarf/scarf": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.1.1.tgz", + "integrity": "sha512-VGbKDbk1RFIaSmdVb0cNjjWJoRWRI/Weo23AjRCC2nryO0iAS8pzsToJfPVPtVs74WHw4L1UTADNdIYRLkirZQ==", + "hasInstallScript": true + }, + "node_modules/@schematics/angular": { + "version": "14.2.1", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-14.2.1.tgz", + "integrity": "sha512-Dchixep/FMETAMuyFchw9Nryi7tfuZQRumzIOtQpv+KaVtfjvcIlES0KuI0U3Qh7tGIYPBmO3Mkt3oojcl2RBA==", + "dev": true, + "dependencies": { + "@angular-devkit/core": "14.2.1", + "@angular-devkit/schematics": "14.2.1", + "jsonc-parser": "3.1.0" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + } + }, + "node_modules/@schematics/angular/node_modules/@angular-devkit/core": { + "version": "14.2.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-14.2.1.tgz", + "integrity": "sha512-lW8oNGuJqr4r31FWBjfWQYkSXdiOHBGOThIEtHvUVBKfPF/oVrupLueCUgBPel+NvxENXdo93uPsqHN7bZbmsQ==", + "dev": true, + "dependencies": { + "ajv": "8.11.0", + "ajv-formats": "2.1.1", + "jsonc-parser": "3.1.0", + "rxjs": "6.6.7", + "source-map": "0.7.4" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0", + "npm": "^6.11.0 || ^7.5.6 || >=8.0.0", + "yarn": ">= 1.13.0" + }, + "peerDependencies": { + "chokidar": "^3.5.2" + }, + "peerDependenciesMeta": { + "chokidar": { + "optional": true + } + } + }, + "node_modules/@schematics/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/@schematics/angular/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/@sideway/address": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz", + "integrity": "sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==", + "dev": true, + "dependencies": { + "@hapi/hoek": "^9.0.0" + } + }, + "node_modules/@sideway/formula": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==", + "dev": true + }, + "node_modules/@sideway/pinpoint": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", + "dev": true + }, + "node_modules/@sinclair/typebox": { + "version": "0.24.40", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.40.tgz", + "integrity": "sha512-Xint60L8rF0+nRy+6fCjW9jQMmu7fTpbwTBrXZiK6eq/RHDJS7LvWX/0oXC8O7fCePmrY/XdfaTv2HiUDeCq4g==", + "dev": true + }, + "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": "9.1.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz", + "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==", + "dev": true, + "dependencies": { + "@sinonjs/commons": "^1.7.0" + } + }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==", + "dev": true + }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "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.18.1", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.1.tgz", + "integrity": "sha512-FSdLaZh2UxaMuLp9lixWaHq/golWTRWOnRsAXzDTDSDOQLuZb1nsdCt6pJSPWSEQt2eFZ2YVk3oYhn+1kLMeMA==", + "dev": true, + "dependencies": { + "@babel/types": "^7.3.0" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dev": true, + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/bonjour": { + "version": "3.5.10", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", + "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/concat-stream": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@types/concat-stream/-/concat-stream-1.6.1.tgz", + "integrity": "sha512-eHE4cQPoj6ngxBZMvVf6Hw7Mh4jMW4U9lpGmS5GBPB9RYxlFg+CHaVN7ErNY4W9XfLIEn20b4VDYaIrbq0q4uA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/connect-history-api-fallback": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz", + "integrity": "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==", + "dev": true, + "dependencies": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, + "node_modules/@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", + "dev": true + }, + "node_modules/@types/cors": { + "version": "2.8.13", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz", + "integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/eslint": { + "version": "8.4.6", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.6.tgz", + "integrity": "sha512-/fqTbjxyFUaYNO7VcW5g+4npmqVACz1bB7RTHYuLj+PRjw9hrCwrUXVQFpChUS0JsyEFvMZ7U/PfmvWgxJhI9g==", + "dev": true, + "dependencies": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "node_modules/@types/eslint-scope": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", + "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", + "dev": true, + "dependencies": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz", + "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==", + "dev": true + }, + "node_modules/@types/expect": { + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/@types/expect/-/expect-1.20.4.tgz", + "integrity": "sha512-Q5Vn3yjTDyCMV50TB6VRIbQNxSE4OmZR86VSbGaNpfUolm0iePBB4KdEEHmxoY5sT2+2DIvXW0rvMDP2nHZ4Mg==", + "dev": true + }, + "node_modules/@types/express": { + "version": "4.17.13", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", + "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==", + "dev": true, + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "4.17.30", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.30.tgz", + "integrity": "sha512-gstzbTWro2/nFed1WXtf+TtrpwxH7Ggs4RLYTLbeVgIkUQOI3WG/JKjgeOU1zXDvezllupjrf8OPIdvTbIaVOQ==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "node_modules/@types/form-data": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-0.0.33.tgz", + "integrity": "sha512-8BSvG1kGm83cyJITQMZSulnl6QV8jqAGreJsc5tPu1Jq0vTSOiY/k24Wx82JRpWwZSqrala6sd5rWi6aNXvqcw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "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/http-proxy": { + "version": "1.17.9", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.9.tgz", + "integrity": "sha512-QsbSjA/fSk7xB+UXlCT3wHBy5ai9wOcNDWwZAtud+jXhwOM3l+EYZh8Lng4+/6n8uar0J7xILzqftJdJ/Wdfkw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "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": "28.1.8", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-28.1.8.tgz", + "integrity": "sha512-8TJkV++s7B6XqnDrzR1m/TT0A0h948Pnl/097veySPN67VRAgQ4gZ7n2KfJo2rVq6njQjdxU3GCCyDvAeuHoiw==", + "dev": true, + "dependencies": { + "expect": "^28.0.0", + "pretty-format": "^28.0.0" + } + }, + "node_modules/@types/jsdom": { + "version": "16.2.15", + "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-16.2.15.tgz", + "integrity": "sha512-nwF87yjBKuX/roqGYerZZM0Nv1pZDMAT5YhOHYeM/72Fic+VEqJh4nyoqoapzJnW3pUlfxPY5FhgsJtM+dRnQQ==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/parse5": "^6.0.3", + "@types/tough-cookie": "*" + } + }, + "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": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, + "node_modules/@types/mime": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", + "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==", + "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.56", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.56.tgz", + "integrity": "sha512-aFcUkv7EddxxOa/9f74DINReQ/celqH8DiB3fRYgVDM2Xm5QJL8sl80QKuAnGvwAsMn+H3IFA6WCrQh1CY7m1A==", + "dev": true + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", + "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", + "dev": true + }, + "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/parse5": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/parse5/-/parse5-6.0.3.tgz", + "integrity": "sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g==", + "dev": true + }, + "node_modules/@types/prettier": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.0.tgz", + "integrity": "sha512-RI1L7N4JnW5gQw2spvL7Sllfuf1SaHdrZpCHiBlCXjIlufi1SMNnbu2teze3/QE67Fg2tBlH7W+mi4hVNk4p0A==", + "dev": true + }, + "node_modules/@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", + "dev": true + }, + "node_modules/@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", + "dev": true + }, + "node_modules/@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "dev": true + }, + "node_modules/@types/semver": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", + "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", + "dev": true + }, + "node_modules/@types/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==", + "dev": true, + "dependencies": { + "@types/express": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==", + "dev": true, + "dependencies": { + "@types/mime": "*", + "@types/node": "*" + } + }, + "node_modules/@types/sinonjs__fake-timers": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz", + "integrity": "sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==", + "dev": true + }, + "node_modules/@types/sizzle": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz", + "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==", + "dev": true + }, + "node_modules/@types/sockjs": { + "version": "0.3.33", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", + "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "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/tough-cookie": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.2.tgz", + "integrity": "sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==", + "dev": true + }, + "node_modules/@types/vinyl": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/vinyl/-/vinyl-2.0.6.tgz", + "integrity": "sha512-ayJ0iOCDNHnKpKTgBG6Q6JOnHTj9zFta+3j2b8Ejza0e4cvRyMn0ZoLEmbPrTHe5YYRlDYPvPWVdV4cTaRyH7g==", + "dev": true, + "dependencies": { + "@types/expect": "^1.20.4", + "@types/node": "*" + } + }, + "node_modules/@types/ws": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", + "integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/yargs": { + "version": "17.0.12", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.12.tgz", + "integrity": "sha512-Nz4MPhecOFArtm81gFQvQqdV7XYCrWKx5uUt6GNHredFHn1i2mtWqXTON7EPXMtNi1qjtjEM/VCHDhcHsAMLXQ==", + "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/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==", + "dev": true, + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.59.7", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.7.tgz", + "integrity": "sha512-BL+jYxUFIbuYwy+4fF86k5vdT9lT0CNJ6HtwrIvGh0PhH8s0yy5rjaKH2fDCrz5ITHy07WCzVGNvAmjJh4IJFA==", + "dev": true, + "dependencies": { + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.59.7", + "@typescript-eslint/type-utils": "5.59.7", + "@typescript-eslint/utils": "5.59.7", + "debug": "^4.3.4", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "semver": "^7.3.7", + "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/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { + "version": "5.59.7", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.7.tgz", + "integrity": "sha512-FL6hkYWK9zBGdxT2wWEd2W8ocXMu3K94i3gvMrjXpx+koFYdYV7KprKfirpgY34vTGzEPPuKoERpP8kD5h7vZQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.59.7", + "@typescript-eslint/visitor-keys": "5.59.7" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": { + "version": "5.59.7", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.7.tgz", + "integrity": "sha512-UnVS2MRRg6p7xOSATscWkKjlf/NDKuqo5TdbWck6rIRZbmKpVNTLALzNvcjIfHBE7736kZOFc/4Z3VcZwuOM/A==", + "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/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": { + "version": "5.59.7", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.7.tgz", + "integrity": "sha512-4A1NtZ1I3wMN2UGDkU9HMBL+TIQfbrh4uS0WDMMpf3xMRursDbqEf1ahh6vAAe3mObt8k3ZATnezwG4pdtWuUQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.59.7", + "@typescript-eslint/visitor-keys": "5.59.7", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "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/eslint-plugin/node_modules/@typescript-eslint/utils": { + "version": "5.59.7", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.59.7.tgz", + "integrity": "sha512-yCX9WpdQKaLufz5luG4aJbOpdXf/fjwGMcLFXZVPUz3QqLirG5QcwwnIHNf8cjLjxK4qtzTO8udUtMQSAToQnQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.59.7", + "@typescript-eslint/types": "5.59.7", + "@typescript-eslint/typescript-estree": "5.59.7", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" + }, + "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/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { + "version": "5.59.7", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.7.tgz", + "integrity": "sha512-tyN+X2jvMslUszIiYbF0ZleP+RqQsFVpGrKI6e0Eet1w8WmhsAtmzaqm8oM8WJQ1ysLwhnsK/4hYHJjOgJVfQQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.59.7", + "eslint-visitor-keys": "^3.3.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/parser": { + "version": "5.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.1.tgz", + "integrity": "sha512-nzjFAN8WEu6yPRDizIFyzAfgK7nybPodMNFGNH0M9tei2gYnYszRDqVA0xlnRjkl7Hkx2vYrEdb6fP2a21cG1g==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.59.1", + "@typescript-eslint/types": "5.59.1", + "@typescript-eslint/typescript-estree": "5.59.1", + "debug": "^4.3.4" + }, + "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.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.1.tgz", + "integrity": "sha512-mau0waO5frJctPuAzcxiNWqJR5Z8V0190FTSqRw1Q4Euop6+zTwHAf8YIXNwDOT29tyUDrQ65jSg9aTU/H0omA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.59.1", + "@typescript-eslint/visitor-keys": "5.59.1" + }, + "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.59.7", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.59.7.tgz", + "integrity": "sha512-ozuz/GILuYG7osdY5O5yg0QxXUAEoI4Go3Do5xeu+ERH9PorHBPSdvD3Tjp2NN2bNLh1NJQSsQu2TPu/Ly+HaQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "5.59.7", + "@typescript-eslint/utils": "5.59.7", + "debug": "^4.3.4", + "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/type-utils/node_modules/@typescript-eslint/scope-manager": { + "version": "5.59.7", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.7.tgz", + "integrity": "sha512-FL6hkYWK9zBGdxT2wWEd2W8ocXMu3K94i3gvMrjXpx+koFYdYV7KprKfirpgY34vTGzEPPuKoERpP8kD5h7vZQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.59.7", + "@typescript-eslint/visitor-keys": "5.59.7" + }, + "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/node_modules/@typescript-eslint/types": { + "version": "5.59.7", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.7.tgz", + "integrity": "sha512-UnVS2MRRg6p7xOSATscWkKjlf/NDKuqo5TdbWck6rIRZbmKpVNTLALzNvcjIfHBE7736kZOFc/4Z3VcZwuOM/A==", + "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/type-utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "5.59.7", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.7.tgz", + "integrity": "sha512-4A1NtZ1I3wMN2UGDkU9HMBL+TIQfbrh4uS0WDMMpf3xMRursDbqEf1ahh6vAAe3mObt8k3ZATnezwG4pdtWuUQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.59.7", + "@typescript-eslint/visitor-keys": "5.59.7", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "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/type-utils/node_modules/@typescript-eslint/utils": { + "version": "5.59.7", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.59.7.tgz", + "integrity": "sha512-yCX9WpdQKaLufz5luG4aJbOpdXf/fjwGMcLFXZVPUz3QqLirG5QcwwnIHNf8cjLjxK4qtzTO8udUtMQSAToQnQ==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.59.7", + "@typescript-eslint/types": "5.59.7", + "@typescript-eslint/typescript-estree": "5.59.7", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" + }, + "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/type-utils/node_modules/@typescript-eslint/visitor-keys": { + "version": "5.59.7", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.7.tgz", + "integrity": "sha512-tyN+X2jvMslUszIiYbF0ZleP+RqQsFVpGrKI6e0Eet1w8WmhsAtmzaqm8oM8WJQ1ysLwhnsK/4hYHJjOgJVfQQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.59.7", + "eslint-visitor-keys": "^3.3.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/types": { + "version": "5.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.1.tgz", + "integrity": "sha512-dg0ICB+RZwHlysIy/Dh1SP+gnXNzwd/KS0JprD3Lmgmdq+dJAJnUPe1gNG34p0U19HvRlGX733d/KqscrGC1Pg==", + "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.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.1.tgz", + "integrity": "sha512-lYLBBOCsFltFy7XVqzX0Ju+Lh3WPIAWxYpmH/Q7ZoqzbscLiCW00LeYCdsUnnfnj29/s1WovXKh2gwCoinHNGA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.59.1", + "@typescript-eslint/visitor-keys": "5.59.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "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.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.29.0.tgz", + "integrity": "sha512-3Eos6uP1nyLOBayc/VUdKZikV90HahXE5Dx9L5YlSd/7ylQPXhLk1BYb29SDgnBnTp+jmSZUU0QxUiyHgW4p7A==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.29.0", + "@typescript-eslint/types": "5.29.0", + "@typescript-eslint/typescript-estree": "5.29.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/utils/node_modules/@typescript-eslint/scope-manager": { + "version": "5.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.29.0.tgz", + "integrity": "sha512-etbXUT0FygFi2ihcxDZjz21LtC+Eps9V2xVx09zFoN44RRHPrkMflidGMI+2dUs821zR1tDS6Oc9IXxIjOUZwA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.29.0", + "@typescript-eslint/visitor-keys": "5.29.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/utils/node_modules/@typescript-eslint/types": { + "version": "5.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.29.0.tgz", + "integrity": "sha512-X99VbqvAXOMdVyfFmksMy3u8p8yoRGITgU1joBJPzeYa0rhdf5ok9S56/itRoUSh99fiDoMtarSIJXo7H/SnOg==", + "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/utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "5.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.29.0.tgz", + "integrity": "sha512-mQvSUJ/JjGBdvo+1LwC+GY2XmSYjK1nAaVw2emp/E61wEVYEyibRHCqm1I1vEKbXCpUKuW4G7u9ZCaZhJbLoNQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.29.0", + "@typescript-eslint/visitor-keys": "5.29.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "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/node_modules/@typescript-eslint/visitor-keys": { + "version": "5.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.29.0.tgz", + "integrity": "sha512-Hpb/mCWsjILvikMQoZIE3voc9wtQcS0A9FUw3h8bhr9UxBdtI/tw1ZDZUOXHXLOVMedKCH5NxyzATwnU78bWCQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.29.0", + "eslint-visitor-keys": "^3.3.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/visitor-keys": { + "version": "5.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.1.tgz", + "integrity": "sha512-6waEYwBTCWryx0VJmP7JaM4FpipLsFl9CvYf2foAE8Qh/Y0s+bxWysciwOs0LTBED4JCaNxTZ5rGadB14M6dwA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.59.1", + "eslint-visitor-keys": "^3.3.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/@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", + "dev": true + }, + "node_modules/abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "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/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.8.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", + "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "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/adjust-sourcemap-loader/node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "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==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/agentkeepalive": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.2.1.tgz", + "integrity": "sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "depd": "^1.1.2", + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.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.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "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-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/angular-calendar": { + "version": "0.30.1", + "resolved": "https://registry.npmjs.org/angular-calendar/-/angular-calendar-0.30.1.tgz", + "integrity": "sha512-QNDGmMzDH/Lx0WXya3mug+MYEpEAQHKlErB/ZpE5F1glChFmPuWCV4aLN6sqacMjZW1TybAJ/ES88B6LS7l8zw==", + "dependencies": { + "@scarf/scarf": "^1.1.1", + "angular-draggable-droppable": "^7.0.0", + "angular-resizable-element": "^6.0.0", + "calendar-utils": "^0.10.1", + "positioning": "^2.0.1", + "tslib": "^2.4.0" + }, + "funding": { + "url": "https://github.com/sponsors/mattlewis92" + }, + "peerDependencies": { + "@angular/core": ">=14.0.0" + } + }, + "node_modules/angular-draggable-droppable": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/angular-draggable-droppable/-/angular-draggable-droppable-7.0.0.tgz", + "integrity": "sha512-XEtIx4hsByrwunKPHiqXRcqCPZlP2YF7ZXWgdoBIGiNVg0L/4xx8OcaKVeVsrOrKsN/t/IB4kJqeG45btUcSGA==", + "dependencies": { + "@mattlewis92/dom-autoscroller": "^2.4.2", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@angular/core": ">=14.0.0" + } + }, + "node_modules/angular-resizable-element": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/angular-resizable-element/-/angular-resizable-element-6.0.2.tgz", + "integrity": "sha512-OEWDfI945UqQMpHfRn/XNrGrOZAXEv6Utzqoc83yAYuNIKJXyTpivcrr/IZsQq735QYLLV/OeDzdP5XilLdMDA==", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/core": ">=14.0.0" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "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==", + "dev": true, + "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": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "dev": true + }, + "node_modules/arch": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", + "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", + "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/are-we-there-yet": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", + "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", + "dev": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "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": "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/array-differ": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-3.0.0.tgz", + "integrity": "sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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-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/arrify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", + "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true + }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "dev": true, + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "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": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", + "dev": true + }, + "node_modules/async-each-series": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/async-each-series/-/async-each-series-0.1.1.tgz", + "integrity": "sha512-p4jj6Fws4Iy2m0iCmI2am2ZNZCgbdgE+P8F/8csmn2vx7ixXrO2zGcuNsD46X5uZSVecmkEy/M06X2vG8KD6dQ==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, + "engines": { + "node": ">= 4.0.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==", + "dev": true, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/autoprefixer": { + "version": "10.4.9", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.9.tgz", + "integrity": "sha512-Uu67eduPEmOeA0vyJby5ghu1AAELCCNSsLAjK+lz6kYzNM5sqnBO36MqfsjhPjQF/BaJM5U/UuFYyl7PavY/wQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + } + ], + "dependencies": { + "browserslist": "^4.21.3", + "caniuse-lite": "^1.0.30001394", + "fraction.js": "^4.2.0", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/aws-sdk": { + "version": "2.1204.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1204.0.tgz", + "integrity": "sha512-H3dRQBdgzAfZ/e/dfiW44fhQrgAuCfIzWhI5y5J9122caI4uZY6TEUd003UXP4nXq2eMfuPWg0bA/mPwbj8RkA==", + "dev": true, + "dependencies": { + "buffer": "4.9.2", + "events": "1.1.1", + "ieee754": "1.1.13", + "jmespath": "0.16.0", + "querystring": "0.2.0", + "sax": "1.2.1", + "url": "0.10.3", + "util": "^0.12.4", + "uuid": "8.0.0", + "xml2js": "0.4.19" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/aws-sdk/node_modules/buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "dev": true, + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "node_modules/aws-sdk/node_modules/ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", + "dev": true + }, + "node_modules/aws-sdk/node_modules/uuid": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", + "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==", + "dev": true, + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/aws4": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", + "dev": true + }, + "node_modules/axios": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", + "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", + "dev": true, + "dependencies": { + "follow-redirects": "^1.14.9", + "form-data": "^4.0.0" + } + }, + "node_modules/axios/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==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/babel-jest": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-28.1.3.tgz", + "integrity": "sha512-epUaPOEWMk3cWX0M/sPvCHHCe9fMFAa/9hXEgKP8nFfNl/jlGkE9ucq9NqkZGXLDduCJYS0UvSlPUwC0S+rH6Q==", + "dev": true, + "dependencies": { + "@jest/transform": "^28.1.3", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^28.1.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-jest/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/babel-jest/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "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/babel-jest/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/babel-jest/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==", + "dev": true + }, + "node_modules/babel-jest/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-jest/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-loader": { + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.5.tgz", + "integrity": "sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==", + "dev": true, + "dependencies": { + "find-cache-dir": "^3.3.1", + "loader-utils": "^2.0.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/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.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-jest-hoist": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-28.1.3.tgz", + "integrity": "sha512-Ys3tUKAmfnkRUpPdpa98eYrAR0nV+sSFUZZEGuQ2EbFd1y4SOLtD5QDNHAq+bb9a+bbXvYQC4b+ID/THIMcU6Q==", + "dev": true, + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.2.tgz", + "integrity": "sha512-LPnodUl3lS0/4wN3Rb+m+UK8s7lj2jcLRrjho4gLw+OJs+I4bvGXshINesY5xx/apM+biTnQ9reDI8yj+0M5+Q==", + "dev": true, + "dependencies": { + "@babel/compat-data": "^7.17.7", + "@babel/helper-define-polyfill-provider": "^0.3.2", + "semver": "^6.1.1" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-polyfill-corejs2/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/babel-plugin-polyfill-corejs3": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.3.tgz", + "integrity": "sha512-zKsXDh0XjnrUEW0mxIHLfjBfnXSMr5Q/goMe/fxpQnLm07mcOZiIZHBNWCMx60HmdvjxfXcalac0tfFg0wqxyw==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.3.2", + "core-js-compat": "^3.21.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/babel-plugin-polyfill-regenerator": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.0.tgz", + "integrity": "sha512-RW1cnryiADFeHmfLS+WW/G431p1PsW5qdRdz0SDRi7TKcUgc7Oh/uXkT7MZ/+tGsT1BkczEAmD5XjUyJ5SWDTw==", + "dev": true, + "dependencies": { + "@babel/helper-define-polyfill-provider": "^0.3.2" + }, + "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": "28.1.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-28.1.3.tgz", + "integrity": "sha512-L+fupJvlWAHbQfn74coNX3zf60LXMJsezNvvx8eIh7iOR1luJ1poxYgQk1F8PYtNq/6QODDHCqsSnTFSWC491A==", + "dev": true, + "dependencies": { + "babel-plugin-jest-hoist": "^28.1.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.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==" + }, + "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/base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "dev": true, + "engines": { + "node": "^4.5.0 || >= 5.9" + } + }, + "node_modules/batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", + "dev": true + }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "dev": true, + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, + "node_modules/before-after-hook": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz", + "integrity": "sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ==", + "dev": true + }, + "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/bin-links": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/bin-links/-/bin-links-3.0.3.tgz", + "integrity": "sha512-zKdnMPWEdh4F5INR07/eBrodC7QrF5JKvqskjz/ZZRXg5YSAZIbn8zGhbhUrElzHBZ2fvEQdOU59RHcTG3GiwA==", + "dev": true, + "dependencies": { + "cmd-shim": "^5.0.0", + "mkdirp-infer-owner": "^2.0.0", + "npm-normalize-package-bin": "^2.0.0", + "read-cmd-shim": "^3.0.0", + "rimraf": "^3.0.0", + "write-file-atomic": "^4.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/bin-links/node_modules/npm-normalize-package-bin": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-2.0.0.tgz", + "integrity": "sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ==", + "dev": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "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==", + "engines": { + "node": ">=8" + } + }, + "node_modules/binaryextensions": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-4.18.0.tgz", + "integrity": "sha512-PQu3Kyv9dM4FnwB7XGj1+HucW+ShvJzJqjuw1JkKVs1mWdwOKVcRjOi+pV9X52A0tNvrPCsPkbFFQb+wE1EAXw==", + "dev": true, + "engines": { + "node": ">=0.8" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, + "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/blob-util": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/blob-util/-/blob-util-2.0.2.tgz", + "integrity": "sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==", + "dev": true + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true + }, + "node_modules/body-parser": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", + "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.10.3", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "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/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/body-parser/node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/body-parser/node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/body-parser/node_modules/qs": { + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", + "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "dev": true, + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/bonjour-service": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.0.14.tgz", + "integrity": "sha512-HIMbgLnk1Vqvs6B4Wq5ep7mxvj9sGz5d1JJyDNSGNIdA/w2MCz6GTjWTdjqOJV1bEPj+6IkxDvWNFKEBxNt4kQ==", + "dev": true, + "dependencies": { + "array-flatten": "^2.1.2", + "dns-equal": "^1.0.0", + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true + }, + "node_modules/bootstrap": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.2.0.tgz", + "integrity": "sha512-qlnS9GL6YZE6Wnef46GxGv1UpGGzAwO0aPL1yOjzDIJpeApeMvqV24iL+pjr2kU4dduoBA9fINKWKgMToobx9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/twbs" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/bootstrap" + } + ], + "peerDependencies": { + "@popperjs/core": "^2.11.5" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "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/browser-sync": { + "version": "2.28.3", + "resolved": "https://registry.npmjs.org/browser-sync/-/browser-sync-2.28.3.tgz", + "integrity": "sha512-gublDeevvAuypnc01SQNGL8fkm4RdIkEagnAJ8Tl9mvr2td3Pl4nVIg5S6fcgoMDEWb8IT7nUHG9YwTATn/k2g==", + "dev": true, + "dependencies": { + "browser-sync-client": "^2.28.3", + "browser-sync-ui": "^2.28.3", + "bs-recipes": "1.3.4", + "bs-snippet-injector": "^2.0.1", + "chalk": "4.1.2", + "chokidar": "^3.5.1", + "connect": "3.6.6", + "connect-history-api-fallback": "^1", + "dev-ip": "^1.0.1", + "easy-extender": "^2.3.4", + "eazy-logger": "^4.0.1", + "etag": "^1.8.1", + "fresh": "^0.5.2", + "fs-extra": "3.0.1", + "http-proxy": "^1.18.1", + "immutable": "^3", + "localtunnel": "^2.0.1", + "micromatch": "^4.0.2", + "opn": "5.3.0", + "portscanner": "2.2.0", + "qs": "^6.11.0", + "raw-body": "^2.3.2", + "resp-modifier": "6.0.2", + "rx": "4.1.0", + "send": "0.16.2", + "serve-index": "1.9.1", + "serve-static": "1.13.2", + "server-destroy": "1.0.1", + "socket.io": "^4.4.1", + "ua-parser-js": "^1.0.33", + "yargs": "^17.3.1" + }, + "bin": { + "browser-sync": "dist/bin.js" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/browser-sync-client": { + "version": "2.28.3", + "resolved": "https://registry.npmjs.org/browser-sync-client/-/browser-sync-client-2.28.3.tgz", + "integrity": "sha512-SMsnGkyXlySVLBWRrXdnTdtQCy0Sl5UoiF8BVtigj9S49DaPWQiesbsyq+uJBUKgpyNve+cvfpBU3KSfIp6oLQ==", + "dev": true, + "dependencies": { + "etag": "1.8.1", + "fresh": "0.5.2", + "mitt": "^1.1.3", + "rxjs": "^5.5.6", + "typescript": "^4.6.2" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/browser-sync-client/node_modules/rxjs": { + "version": "5.5.12", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.12.tgz", + "integrity": "sha512-xx2itnL5sBbqeeiVgNPVuQQ1nC8Jp2WfNJhXWHmElW9YmrpS9UVnNzhP3EH3HFqexO5Tlp8GhYY+WEcqcVMvGw==", + "dev": true, + "dependencies": { + "symbol-observable": "1.0.1" + }, + "engines": { + "npm": ">=2.0.0" + } + }, + "node_modules/browser-sync-client/node_modules/symbol-observable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", + "integrity": "sha512-Kb3PrPYz4HanVF1LVGuAdW6LoVgIwjUYJGzFe7NDrBLCN4lsV/5J0MFurV+ygS4bRVwrCEt2c7MQ1R2a72oJDw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/browser-sync-ui": { + "version": "2.28.3", + "resolved": "https://registry.npmjs.org/browser-sync-ui/-/browser-sync-ui-2.28.3.tgz", + "integrity": "sha512-Mj5M+O3jroGp5hlO6pDfUo19wzUTIuvGyzaRrJAYUgsSkpFacrX+MLCjN9VbZm9fYXbtHyIsnIUUIlYag87wgQ==", + "dev": true, + "dependencies": { + "async-each-series": "0.1.1", + "chalk": "4.1.2", + "connect-history-api-fallback": "^1", + "immutable": "^3", + "server-destroy": "1.0.1", + "socket.io-client": "^4.4.1", + "stream-throttle": "^0.1.3" + } + }, + "node_modules/browser-sync-ui/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/browser-sync-ui/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "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/browser-sync-ui/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/browser-sync-ui/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==", + "dev": true + }, + "node_modules/browser-sync-ui/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-sync-ui/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-sync-webpack-plugin": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/browser-sync-webpack-plugin/-/browser-sync-webpack-plugin-2.3.0.tgz", + "integrity": "sha512-MDvuRrTCtoL11dTdwMymo9CNJvYxJoW67gOO61cThfzHNX40S5WcBU+0bVQ86ll7r7aNpNgyzxF7RtnXMTDbyA==", + "dev": true, + "dependencies": { + "lodash": "^4" + }, + "peerDependencies": { + "browser-sync": "^2", + "webpack": "^1 || ^2 || ^3 || ^4 || ^5" + } + }, + "node_modules/browser-sync/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/browser-sync/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "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/browser-sync/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/browser-sync/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==", + "dev": true + }, + "node_modules/browser-sync/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-sync/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.3.tgz", + "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001370", + "electron-to-chromium": "^1.4.202", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.5" + }, + "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/bs-recipes": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/bs-recipes/-/bs-recipes-1.3.4.tgz", + "integrity": "sha512-BXvDkqhDNxXEjeGM8LFkSbR+jzmP/CYpCiVKYn+soB1dDldeU15EBNDkwVXndKuX35wnNUaPd0qSoQEAkmQtMw==", + "dev": true + }, + "node_modules/bs-snippet-injector": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/bs-snippet-injector/-/bs-snippet-injector-2.0.1.tgz", + "integrity": "sha512-4u8IgB+L9L+S5hknOj3ddNSb42436gsnGm1AuM15B7CdbkpQTyVWgIM5/JUBiKiRwGOR86uo0Lu/OsX+SAlJmw==", + "dev": true + }, + "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": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "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==", + "dev": true + }, + "node_modules/builtins": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", + "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", + "dev": true, + "dependencies": { + "semver": "^7.0.0" + } + }, + "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/cacache": { + "version": "16.1.2", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-16.1.2.tgz", + "integrity": "sha512-Xx+xPlfCZIUHagysjjOAje9nRo8pRDczQCcXb4J2O0BLtH+xeVue6ba4y1kfJfQMAnM2mkcoMIAyOctlaRGWYA==", + "dev": true, + "dependencies": { + "@npmcli/fs": "^2.1.0", + "@npmcli/move-file": "^2.0.0", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "glob": "^8.0.1", + "infer-owner": "^1.0.4", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "mkdirp": "^1.0.4", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^9.0.0", + "tar": "^6.1.11", + "unique-filename": "^1.1.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/cachedir": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.3.0.tgz", + "integrity": "sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/calendar-utils": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/calendar-utils/-/calendar-utils-0.10.4.tgz", + "integrity": "sha512-gBK4xCJ42yjaUKwuUha6cZOfxAmGzvSgbdAaX3xLRioeKbYoOK1x1qeD6dch72rsMZlTgATPbBBx42bnkStqgQ==" + }, + "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-lite": { + "version": "1.0.30001397", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001397.tgz", + "integrity": "sha512-SW9N2TbCdLf0eiNDRrrQXx2sOkaakNZbCjgNpPyMJJbiOrU5QzMIrXOVMRM1myBXTD5iTkdrtU/EguCrBocHlA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + } + ] + }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", + "dev": true + }, + "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==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "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==", + "dev": true + }, + "node_modules/chart.js": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.9.1.tgz", + "integrity": "sha512-Ro2JbLmvg83gXF5F4sniaQ+lTbSv18E+TIf2cOeiH1Iqd2PGFOtem+DUufMZsCJwFE7ywPOpfXFBwRTGq7dh6w==", + "peer": true + }, + "node_modules/check-more-types": { + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz", + "integrity": "sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/chevrotain": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-10.3.0.tgz", + "integrity": "sha512-sy3yTBfvNJmxzYOGWaDStZFuA7va5/MXwzBU+XBIol0bR0aYlfGqTjzgedeu32Ki/j7IVyvYAZO2PInBjmmMjg==", + "dev": true, + "dependencies": { + "@chevrotain/cst-dts-gen": "10.3.0", + "@chevrotain/gast": "10.3.0", + "@chevrotain/types": "10.3.0", + "@chevrotain/utils": "10.3.0", + "lodash": "4.17.21", + "regexp-to-ast": "0.5.0" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "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.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.4.0.tgz", + "integrity": "sha512-t5QdPT5jq3o262DOQ8zA6E1tlH2upmUc4Hlvrbx1pGYJuiiHl7O7rvVNI+l8HTVhd/q3Qc9vqimkNk5yiXsAug==", + "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/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-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz", + "integrity": "sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-table": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.11.tgz", + "integrity": "sha512-IqLQi4lO0nIB4tcdTpN4LCB9FI3uqrJZK7RC515EnhZ6qBaglkIgICb1wjeAqpdoOabm1+SuQtkXIPdYC93jhQ==", + "dev": true, + "dependencies": { + "colors": "1.0.3" + }, + "engines": { + "node": ">= 0.2.0" + } + }, + "node_modules/cli-table3": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.2.tgz", + "integrity": "sha512-QyavHCaIC80cMivimWu4aWHilIpiDpfm3hGmqAmXVL1UsnbLuBSMd21hTX6VY4ZSDSM73ESLeF8TOYId3rBTbw==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "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==", + "dev": true, + "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==", + "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": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clone-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", + "integrity": "sha512-KLLTJWrvwIP+OPfMn0x2PheDEP20RPUcGXj/ERegTgdmPEZylALQldygiqrPPu8P45uNuPs7ckmReLY6v/iA5g==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "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-stats": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", + "integrity": "sha512-au6ydSpg6nsrigcZ4m8Bc9hxjeW+GJ8xh5G3BJCMt4WXe1H10UNaVOamqQTmrx1kjVuxAHIQSNU6hY4Nsn9/ag==", + "dev": true + }, + "node_modules/cloneable-readable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz", + "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "process-nextick-args": "^2.0.0", + "readable-stream": "^2.3.5" + } + }, + "node_modules/cloneable-readable/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/cloneable-readable/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/cmd-shim": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cmd-shim/-/cmd-shim-5.0.0.tgz", + "integrity": "sha512-qkCtZ59BidfEwHltnJwkyVZn+XQojdAySM1D1gSeh11Z4pW1Kpolkyo53L5noc0nrxmIvyFwTmJRo4xs7FFLPw==", + "dev": true, + "dependencies": { + "mkdirp-infer-owner": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "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/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, + "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==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dev": true, + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true, + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/colorette": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", + "dev": true + }, + "node_modules/colors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "integrity": "sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw==", + "dev": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/colorspace": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", + "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", + "dev": true, + "dependencies": { + "color": "^3.1.3", + "text-hex": "1.0.x" + } + }, + "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==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/common-ancestor-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz", + "integrity": "sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==", + "dev": true + }, + "node_modules/common-tags": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", + "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "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/bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "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": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "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==", + "dev": true, + "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==", + "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/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==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/concurrently": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-8.2.0.tgz", + "integrity": "sha512-nnLMxO2LU492mTUj9qX/az/lESonSZu81UznYDoXtz1IQf996ixVqPAgHXwvHiHCAef/7S8HIK+fTFK7Ifk8YA==", + "dev": true, + "dependencies": { + "chalk": "^4.1.2", + "date-fns": "^2.30.0", + "lodash": "^4.17.21", + "rxjs": "^7.8.1", + "shell-quote": "^1.8.1", + "spawn-command": "0.0.2", + "supports-color": "^8.1.1", + "tree-kill": "^1.2.2", + "yargs": "^17.7.2" + }, + "bin": { + "conc": "dist/bin/concurrently.js", + "concurrently": "dist/bin/concurrently.js" + }, + "engines": { + "node": "^14.13.0 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/open-cli-tools/concurrently?sponsor=1" + } + }, + "node_modules/concurrently/node_modules/@babel/runtime": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.5.tgz", + "integrity": "sha512-ecjvYlnAaZ/KVneE/OdKYBYfgXV3Ptu6zQWmgEF7vwKhQnvVS6bjMD2XYgj+SNvQ1GfK/pjgokfPkC/2CO8CuA==", + "dev": true, + "dependencies": { + "regenerator-runtime": "^0.13.11" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/concurrently/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/concurrently/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "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/concurrently/node_modules/chalk/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/concurrently/node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/concurrently/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/concurrently/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==", + "dev": true + }, + "node_modules/concurrently/node_modules/date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.21.0" + }, + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, + "node_modules/concurrently/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/concurrently/node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "dev": true + }, + "node_modules/concurrently/node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "dependencies": { + "tslib": "^2.1.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/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "dependencies": { + "cliui": "^8.0.1", + "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.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/conf": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/conf/-/conf-10.2.0.tgz", + "integrity": "sha512-8fLl9F04EJqjSqH+QjITQfJF8BrOVaYr1jewVgSRAEWePfxT0sku4w2hrGQ60BC/TNLGQ2pgxNlTbWQmMPFvXg==", + "dev": true, + "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/connect": { + "version": "3.6.6", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.6.6.tgz", + "integrity": "sha512-OO7axMmPpu/2XuX1+2Yrg0ddju31B6xLZMWkJ5rYBu4YRmRVlOjvlY6kw2FJKiAzyxGwnrDUAG4s1Pf0sbBMCQ==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "finalhandler": "1.1.0", + "parseurl": "~1.3.2", + "utils-merge": "1.0.1" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "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/connect/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/connect/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "dev": true + }, + "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==", + "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": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "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-webpack-plugin": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz", + "integrity": "sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==", + "dev": true, + "dependencies": { + "fast-glob": "^3.2.11", + "glob-parent": "^6.0.1", + "globby": "^13.1.1", + "normalize-path": "^3.0.0", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + } + }, + "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/globby": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.2.tgz", + "integrity": "sha512-LKSDZXToac40u8Q1PQtZihbNdTYSNMuWe+K5l+oa6KgDzSvVrHXlJy40hUP522RjAIoNLJYBJi7ow+rbFpIhHQ==", + "dev": true, + "dependencies": { + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.11", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/copy-webpack-plugin/node_modules/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/copy-webpack-plugin/node_modules/slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/core-js-compat": { + "version": "3.25.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.25.1.tgz", + "integrity": "sha512-pOHS7O0i8Qt4zlPW/eIFjwp+NrTPx+wTL0ctgI2fHn31sZOq89rDsmtc/A2vAX7r6shl+bmVI+678He46jgBlw==", + "dev": true, + "dependencies": { + "browserslist": "^4.21.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "dev": true + }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dev": true, + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "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/cosmiconfig/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/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.16", + "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.16.tgz", + "integrity": "sha512-JwjgmO6i3y6RWtLYmXwO5jMd+maZt8Tnfu7VVISmEWyQqfLpB8soBswf8/2bu6SBXxtKA68Al3c+qIG1ApT68A==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "css-select": "^4.2.0", + "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/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/critters/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "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/critters/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/critters/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==", + "dev": true + }, + "node_modules/critters/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/critters/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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/css-blank-pseudo": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-3.0.3.tgz", + "integrity": "sha512-VS90XWtsHGqoM0t4KpH053c4ehxZ2E6HtGI7x68YFV0pTo/QmkV/YFA+NnlvK8guxZVNWGQhVNJGC39Q8XF4OQ==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "bin": { + "css-blank-pseudo": "dist/cli.cjs" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/css-has-pseudo": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-3.0.4.tgz", + "integrity": "sha512-Vse0xpR1K9MNlp2j5w1pgWIJtm1a8qS0JwS9goFYcImjlHEmywP9VUF05aGBXzGpDJF86QXk4L0ypBmwPhGArw==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "bin": { + "css-has-pseudo": "dist/cli.cjs" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/css-loader": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.1.tgz", + "integrity": "sha512-yB5CNFa14MbPJcomwNh3wLThtkZgcNyI2bNMRt8iE5Z8Vwl7f8vQXFAzn2HDOJvtDq2NTZBUGMSUNNyrv3/+cw==", + "dev": true, + "dependencies": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.7", + "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.2.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-prefers-color-scheme": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz", + "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==", + "dev": true, + "bin": { + "css-prefers-color-scheme": "dist/cli.cjs" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "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-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/cssdb": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.0.1.tgz", + "integrity": "sha512-pT3nzyGM78poCKLAEy2zWIVX2hikq6dIrjuZzLV98MumBg+xMTNYfHx7paUlfiRTgg91O/vR889CIf+qiv79Rw==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + }, + "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/cssom": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", + "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/cypress": { + "version": "12.16.0", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-12.16.0.tgz", + "integrity": "sha512-mwv1YNe48hm0LVaPgofEhGCtLwNIQEjmj2dJXnAkY1b4n/NE9OtgPph4TyS+tOtYp5CKtRmDvBzWseUXQTjbTg==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "@cypress/request": "^2.88.10", + "@cypress/xvfb": "^1.2.4", + "@types/node": "^14.14.31", + "@types/sinonjs__fake-timers": "8.1.1", + "@types/sizzle": "^2.3.2", + "arch": "^2.2.0", + "blob-util": "^2.0.2", + "bluebird": "^3.7.2", + "buffer": "^5.6.0", + "cachedir": "^2.3.0", + "chalk": "^4.1.0", + "check-more-types": "^2.24.0", + "cli-cursor": "^3.1.0", + "cli-table3": "~0.6.1", + "commander": "^6.2.1", + "common-tags": "^1.8.0", + "dayjs": "^1.10.4", + "debug": "^4.3.4", + "enquirer": "^2.3.6", + "eventemitter2": "6.4.7", + "execa": "4.1.0", + "executable": "^4.1.1", + "extract-zip": "2.0.1", + "figures": "^3.2.0", + "fs-extra": "^9.1.0", + "getos": "^3.2.1", + "is-ci": "^3.0.0", + "is-installed-globally": "~0.4.0", + "lazy-ass": "^1.6.0", + "listr2": "^3.8.3", + "lodash": "^4.17.21", + "log-symbols": "^4.0.0", + "minimist": "^1.2.8", + "ospath": "^1.2.2", + "pretty-bytes": "^5.6.0", + "proxy-from-env": "1.0.0", + "request-progress": "^3.0.0", + "semver": "^7.3.2", + "supports-color": "^8.1.1", + "tmp": "~0.2.1", + "untildify": "^4.0.0", + "yauzl": "^2.10.0" + }, + "bin": { + "cypress": "bin/cypress" + }, + "engines": { + "node": "^14.0.0 || ^16.0.0 || >=18.0.0" + } + }, + "node_modules/cypress/node_modules/@types/node": { + "version": "14.18.28", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.28.tgz", + "integrity": "sha512-CK2fnrQlIgKlCV3N2kM+Gznb5USlwA1KFX3rJVHmgVk6NJxFPuQ86pAcvKnu37IA4BGlSRz7sEE1lHL1aLZ/eQ==", + "dev": true + }, + "node_modules/cypress/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cypress/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "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/cypress/node_modules/chalk/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cypress/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/cypress/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==", + "dev": true + }, + "node_modules/cypress/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cypress/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/cypress/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/cypress/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/cypress/node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/dargs": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz", + "integrity": "sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "dev": true, + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/data-urls": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", + "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", + "dev": true, + "dependencies": { + "abab": "^2.0.6", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/data-urls/node_modules/whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "dev": true, + "dependencies": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/date-fns": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz", + "integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==" + }, + "node_modules/dateformat": { + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz", + "integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/dayjs": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.5.tgz", + "integrity": "sha512-CAdX5Q3YW3Gclyo5Vpqkgpj8fSdLQcRuzfX6mC6Phy0nfJ0eGYOeS7m4mt2plDWLAtA4TqTakvbboHvUxfe4iA==" + }, + "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==", + "dev": true, + "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/debuglog": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz", + "integrity": "sha512-syBZ+rnAK3EgMsH2aYEOLUW7mZSY9Gb+0wUMCFsZvcmiz+HigA0LOcq/HoQqVuGG+EKykunc7QG2bzrponfaSw==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/decimal.js": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.0.tgz", + "integrity": "sha512-Nv6ENEzyPQ6AItkGwLE2PGKinZZ9g59vSh2BeH6NqPu0OTKZ5ruJsVqh/orbAnqXc9pBbgXAIrc2EyaCj8NpGg==", + "dev": true + }, + "node_modules/dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", + "dev": true + }, + "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": "6.0.3", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", + "dev": true, + "dependencies": { + "execa": "^5.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/default-gateway/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/default-gateway/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/default-gateway/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/defaults": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha512-s82itHOnYrN0Ib8r+z7laQz3sdE+4FP3d9Q7VLO7U+KRT+CR0GsWuyHxzdAY82I7cXv0G/twrqomTJLOssO5HA==", + "dev": true, + "dependencies": { + "clone": "^1.0.2" + } + }, + "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.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "dev": true, + "dependencies": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "dev": true + }, + "node_modules/depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "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==", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", + "dev": true + }, + "node_modules/destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha512-3NdhDuEXnfun/z7x9GOElY49LoqVHoGScmOKwmxhsS8N5Y+Z8KyPPDnaSzqWgYt/ji4mqwfTS34Htrk0zPIXVg==", + "dev": true + }, + "node_modules/detect-indent": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-7.0.1.tgz", + "integrity": "sha512-Mc7QhQ8s+cLrnUfU/Ji94vG/r8M26m8f++vyres4ZoojaRDpZ1eSIh/EpzLNwlWuvzSZ3UbDFspjFvTDXe6e/g==", + "dev": true, + "engines": { + "node": ">=12.20" + } + }, + "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==", + "dev": true + }, + "node_modules/dev-ip": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dev-ip/-/dev-ip-1.0.1.tgz", + "integrity": "sha512-LmVkry/oDShEgSZPNgqCIp2/TlqtExeGmymru3uCELnfyjY11IzpAproLYs+1X88fXO6DBoYP3ul2Xo2yz2j6A==", + "dev": true, + "bin": { + "dev-ip": "lib/dev-ip.js" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/dezalgo": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", + "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", + "dev": true, + "dependencies": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": 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": "28.1.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-28.1.1.tgz", + "integrity": "sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==", + "dev": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.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": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==", + "dev": true + }, + "node_modules/dns-packet": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.4.0.tgz", + "integrity": "sha512-EgqGeaBB8hLiHLZtp/IbaDQTL8pZ0+IvwzSHA6d7VyMDM+B9hgddEMa9xjK5oYnw0ci0JQ6g2XCD7/f6cafU6g==", + "dev": true, + "dependencies": { + "@leichtgewicht/ip-codec": "^2.0.1" + }, + "engines": { + "node": ">=6" + } + }, + "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.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "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.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domexception": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", + "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", + "dev": true, + "dependencies": { + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "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": "6.0.1", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", + "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==", + "dev": true, + "dependencies": { + "is-obj": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/drange": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/drange/-/drange-1.1.1.tgz", + "integrity": "sha512-pYxfDYpued//QpnLIm4Avk7rsNtAtQkUES2cwAYSvD/wd2pKD71gN2Ebj3e7klzXwjocvE8c5vx/1fxwpqmSxA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "dev": true + }, + "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/easy-extender": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/easy-extender/-/easy-extender-2.3.4.tgz", + "integrity": "sha512-8cAwm6md1YTiPpOvDULYJL4ZS6WfM5/cTeVVh4JsvyYZAoqlRVUpHL9Gr5Fy7HA6xcSZicUia3DeAgO3Us8E+Q==", + "dev": true, + "dependencies": { + "lodash": "^4.17.10" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/eazy-logger": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/eazy-logger/-/eazy-logger-4.0.1.tgz", + "integrity": "sha512-2GSFtnnC6U4IEKhEI7+PvdxrmjJ04mdsj3wHZTFiw0tUtG4HCWzTr13ZYTk8XOGnA1xQMaDljoBOYlk3D/MMSw==", + "dev": true, + "dependencies": { + "chalk": "4.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/eazy-logger/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/eazy-logger/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "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/eazy-logger/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/eazy-logger/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==", + "dev": true + }, + "node_modules/eazy-logger/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eazy-logger/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "dev": true, + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true + }, + "node_modules/ejs": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.8.tgz", + "integrity": "sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==", + "dev": true, + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.4.247", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.247.tgz", + "integrity": "sha512-FLs6R4FQE+1JHM0hh3sfdxnYjKvJpHZyhQDjc2qFq/xFvmmRt/TATNToZhrcGUFzpF2XjeiuozrA8lI0PZmYYw==" + }, + "node_modules/emittery": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.2.tgz", + "integrity": "sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "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/enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", + "dev": true + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "dev": true, + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/encoding/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, + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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==", + "dev": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/engine.io": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.4.2.tgz", + "integrity": "sha512-FKn/3oMiJjrOEOeUub2WCox6JhxBXq/Zn3fZOMCBxKnNYtsdKjxhl7yR3fZhM9PV+rdE75SU5SYMc+2PGzo+Tg==", + "dev": true, + "dependencies": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.0.3", + "ws": "~8.11.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/engine.io-client": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.4.0.tgz", + "integrity": "sha512-GyKPDyoEha+XZ7iEqam49vz6auPnNJ9ZBfy89f+rMMas8AuiMWOZ9PVzu8xb9ZC6rafUqiGHSCfu22ih66E+1g==", + "dev": true, + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.0.3", + "ws": "~8.11.0", + "xmlhttprequest-ssl": "~2.0.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz", + "integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==", + "dev": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz", + "integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "dev": true + }, + "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, + "optional": true, + "dependencies": { + "prr": "~1.0.1" + }, + "bin": { + "errno": "cli.js" + } + }, + "node_modules/error": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/error/-/error-10.4.0.tgz", + "integrity": "sha512-YxIFEJuhgcICugOUvRx5th0UM+ActZ9sjY0QJmeVwsQdvosZ7kYzc9QqS0Da3R5iUmgU5meGIxh0xBeZpMVeLw==", + "dev": true + }, + "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.20.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.2.tgz", + "integrity": "sha512-XxXQuVNrySBNlEkTYJoDNFe5+s2yIOpzq80sUHEdPdQr0S5nTLz4ZPPPswNIpKseDDUS5yghX1gfLIHQZ1iNuQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.1.2", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "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.2", + "is-string": "^1.0.7", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.2", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.4.3", + "string.prototype.trimend": "^1.0.5", + "string.prototype.trimstart": "^1.0.5", + "unbox-primitive": "^1.0.2" + }, + "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 + }, + "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/esbuild": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.5.tgz", + "integrity": "sha512-VSf6S1QVqvxfIsSKb3UKr3VhUCis7wgDbtF4Vd9z84UJr05/Sp2fRKmzC+CSPG/dNAPPJZ0BTBLTT1Fhd6N9Gg==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/linux-loong64": "0.15.5", + "esbuild-android-64": "0.15.5", + "esbuild-android-arm64": "0.15.5", + "esbuild-darwin-64": "0.15.5", + "esbuild-darwin-arm64": "0.15.5", + "esbuild-freebsd-64": "0.15.5", + "esbuild-freebsd-arm64": "0.15.5", + "esbuild-linux-32": "0.15.5", + "esbuild-linux-64": "0.15.5", + "esbuild-linux-arm": "0.15.5", + "esbuild-linux-arm64": "0.15.5", + "esbuild-linux-mips64le": "0.15.5", + "esbuild-linux-ppc64le": "0.15.5", + "esbuild-linux-riscv64": "0.15.5", + "esbuild-linux-s390x": "0.15.5", + "esbuild-netbsd-64": "0.15.5", + "esbuild-openbsd-64": "0.15.5", + "esbuild-sunos-64": "0.15.5", + "esbuild-windows-32": "0.15.5", + "esbuild-windows-64": "0.15.5", + "esbuild-windows-arm64": "0.15.5" + } + }, + "node_modules/esbuild-android-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.5.tgz", + "integrity": "sha512-dYPPkiGNskvZqmIK29OPxolyY3tp+c47+Fsc2WYSOVjEPWNCHNyqhtFqQadcXMJDQt8eN0NMDukbyQgFcHquXg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-android-arm64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.5.tgz", + "integrity": "sha512-YyEkaQl08ze3cBzI/4Cm1S+rVh8HMOpCdq8B78JLbNFHhzi4NixVN93xDrHZLztlocEYqi45rHHCgA8kZFidFg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-darwin-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.5.tgz", + "integrity": "sha512-Cr0iIqnWKx3ZTvDUAzG0H/u9dWjLE4c2gTtRLz4pqOBGjfjqdcZSfAObFzKTInLLSmD0ZV1I/mshhPoYSBMMCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-darwin-arm64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.5.tgz", + "integrity": "sha512-WIfQkocGtFrz7vCu44ypY5YmiFXpsxvz2xqwe688jFfSVCnUsCn2qkEVDo7gT8EpsLOz1J/OmqjExePL1dr1Kg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-freebsd-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.5.tgz", + "integrity": "sha512-M5/EfzV2RsMd/wqwR18CELcenZ8+fFxQAAEO7TJKDmP3knhWSbD72ILzrXFMMwshlPAS1ShCZ90jsxkm+8FlaA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-freebsd-arm64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.5.tgz", + "integrity": "sha512-2JQQ5Qs9J0440F/n/aUBNvY6lTo4XP/4lt1TwDfHuo0DY3w5++anw+jTjfouLzbJmFFiwmX7SmUhMnysocx96w==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-32": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.5.tgz", + "integrity": "sha512-gO9vNnIN0FTUGjvTFucIXtBSr1Woymmx/aHQtuU+2OllGU6YFLs99960UD4Dib1kFovVgs59MTXwpFdVoSMZoQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.5.tgz", + "integrity": "sha512-ne0GFdNLsm4veXbTnYAWjbx3shpNKZJUd6XpNbKNUZaNllDZfYQt0/zRqOg0sc7O8GQ+PjSMv9IpIEULXVTVmg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-arm": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.5.tgz", + "integrity": "sha512-wvAoHEN+gJ/22gnvhZnS/+2H14HyAxM07m59RSLn3iXrQsdS518jnEWRBnJz3fR6BJa+VUTo0NxYjGaNt7RA7Q==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-arm64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.5.tgz", + "integrity": "sha512-7EgFyP2zjO065XTfdCxiXVEk+f83RQ1JsryN1X/VSX2li9rnHAt2swRbpoz5Vlrl6qjHrCmq5b6yxD13z6RheA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-mips64le": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.5.tgz", + "integrity": "sha512-KdnSkHxWrJ6Y40ABu+ipTZeRhFtc8dowGyFsZY5prsmMSr1ZTG9zQawguN4/tunJ0wy3+kD54GaGwdcpwWAvZQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-ppc64le": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.5.tgz", + "integrity": "sha512-QdRHGeZ2ykl5P0KRmfGBZIHmqcwIsUKWmmpZTOq573jRWwmpfRmS7xOhmDHBj9pxv+6qRMH8tLr2fe+ZKQvCYw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-riscv64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.5.tgz", + "integrity": "sha512-p+WE6RX+jNILsf+exR29DwgV6B73khEQV0qWUbzxaycxawZ8NE0wA6HnnTxbiw5f4Gx9sJDUBemh9v49lKOORA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-linux-s390x": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.5.tgz", + "integrity": "sha512-J2ngOB4cNzmqLHh6TYMM/ips8aoZIuzxJnDdWutBw5482jGXiOzsPoEF4j2WJ2mGnm7FBCO4StGcwzOgic70JQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-netbsd-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.5.tgz", + "integrity": "sha512-MmKUYGDizYjFia0Rwt8oOgmiFH7zaYlsoQ3tIOfPxOqLssAsEgG0MUdRDm5lliqjiuoog8LyDu9srQk5YwWF3w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-openbsd-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.5.tgz", + "integrity": "sha512-2mMFfkLk3oPWfopA9Plj4hyhqHNuGyp5KQyTT9Rc8hFd8wAn5ZrbJg+gNcLMo2yzf8Uiu0RT6G9B15YN9WQyMA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-sunos-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.5.tgz", + "integrity": "sha512-2sIzhMUfLNoD+rdmV6AacilCHSxZIoGAU2oT7XmJ0lXcZWnCvCtObvO6D4puxX9YRE97GodciRGDLBaiC6x1SA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-wasm": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.15.5.tgz", + "integrity": "sha512-lTJOEKekN/4JI/eOEq0wLcx53co2N6vaT/XjBz46D1tvIVoUEyM0o2K6txW6gEotf31szFD/J1PbxmnbkGlK9A==", + "dev": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-windows-32": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.5.tgz", + "integrity": "sha512-e+duNED9UBop7Vnlap6XKedA/53lIi12xv2ebeNS4gFmu7aKyTrok7DPIZyU5w/ftHD4MUDs5PJUkQPP9xJRzg==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-windows-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.5.tgz", + "integrity": "sha512-v+PjvNtSASHOjPDMIai9Yi+aP+Vwox+3WVdg2JB8N9aivJ7lyhp4NVU+J0MV2OkWFPnVO8AE/7xH+72ibUUEnw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/esbuild-windows-arm64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.5.tgz", + "integrity": "sha512-Yz8w/D8CUPYstvVQujByu6mlf48lKmXkq6bkeSZZxTA626efQOJb26aDGLzmFWx6eg/FwrXgt6SZs9V8Pwy/aA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true + }, + "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": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "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": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "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": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "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": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "dev": true, + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/eslint": { + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.42.0.tgz", + "integrity": "sha512-ulg9Ms6E1WPf67PHaEY4/6E2tEn5/f7FXGzr3t9cBMugOmf1INYvuUwwh1aXQN4MfJ6a5K2iNwP3w4AColvI9A==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.4.0", + "@eslint/eslintrc": "^2.0.3", + "@eslint/js": "8.42.0", + "@humanwhocodes/config-array": "^0.11.10", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "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.2.0", + "eslint-visitor-keys": "^3.4.1", + "espree": "^9.5.2", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0" + }, + "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.8.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz", + "integrity": "sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==", + "dev": true, + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-cypress": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-cypress/-/eslint-plugin-cypress-2.12.1.tgz", + "integrity": "sha512-c2W/uPADl5kospNDihgiLc7n87t5XhUbFDoTl6CfVkmG+kDAb5Ux10V9PoLPu9N+r7znpc+iQlcmAqT1A/89HA==", + "dev": true, + "dependencies": { + "globals": "^11.12.0" + }, + "peerDependencies": { + "eslint": ">= 3.2.1" + } + }, + "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.4.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", + "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-webpack-plugin": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/eslint-webpack-plugin/-/eslint-webpack-plugin-3.2.0.tgz", + "integrity": "sha512-avrKcGncpPbPSUHX6B3stNGzkKFto3eL+DKM4+VyMrVnhPc3vRczVlCq3uhuFOdRvDHTVXuzwk1ZKUrqDQHQ9w==", + "dev": true, + "dependencies": { + "@types/eslint": "^7.29.0 || ^8.4.1", + "jest-worker": "^28.0.2", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "schema-utils": "^4.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0", + "webpack": "^5.0.0" + } + }, + "node_modules/eslint-webpack-plugin/node_modules/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "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/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/eslint/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==", + "dev": true + }, + "node_modules/eslint/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/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "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/eslint/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/eslint/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==", + "dev": true + }, + "node_modules/eslint/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==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", + "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "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/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/eslint/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==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "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/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/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/eslint/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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.5.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", + "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", + "dev": true, + "dependencies": { + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "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.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "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": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "engines": { + "node": ">= 0.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/eventemitter2": { + "version": "6.4.7", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.7.tgz", + "integrity": "sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==", + "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": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==", + "dev": true, + "engines": { + "node": ">=0.4.x" + } + }, + "node_modules/execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/executable": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz", + "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==", + "dev": true, + "dependencies": { + "pify": "^2.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/expect/-/expect-28.1.3.tgz", + "integrity": "sha512-eEh0xn8HlsuOBxFgIss+2mX85VAS4Qy3OSkjV7rlBWljtA4oWH37glVGyOZSZvErDT/yBywZdPGwCXuTvSG85g==", + "dev": true, + "dependencies": { + "@jest/expect-utils": "^28.1.3", + "jest-get-type": "^28.0.2", + "jest-matcher-utils": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-util": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/express": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz", + "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==", + "dev": true, + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.0", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.10.3", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "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": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "dev": true + }, + "node_modules/express/node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "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/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/express/node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/express/node_modules/finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/express/node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/express/node_modules/qs": { + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", + "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "dev": true, + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/express/node_modules/send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/express/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/express/node_modules/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dev": true, + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/express/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "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==", + "dev": true, + "dependencies": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/external-editor/node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", + "dev": true, + "engines": [ + "node >=0.6.0" + ] + }, + "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==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "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": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "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": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", + "dev": true + }, + "node_modules/figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dev": true, + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "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==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz", + "integrity": "sha512-ejnvM9ZXYzp6PUPUyQBMBf0Co5VX2gr5H2VQe2Ui2jWXNlxv+PYZo8wpAymJNJdLsG1R4p+M4aynF8KuoUEwRw==", + "dev": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.1", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "statuses": "~1.3.1", + "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": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "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": "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/find-yarn-workspace-root2": { + "version": "1.2.16", + "resolved": "https://registry.npmjs.org/find-yarn-workspace-root2/-/find-yarn-workspace-root2-1.2.16.tgz", + "integrity": "sha512-hr6hb1w8ePMpPVUK39S4RlwJzi+xPLuVuG8XlwXU3KD5Yn3qgBWVfy3AzNlDhWvE1EORCE65/Qm26rFQt3VLVA==", + "dev": true, + "dependencies": { + "micromatch": "^4.0.2", + "pkg-dir": "^4.2.0" + } + }, + "node_modules/first-chunk-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-2.0.0.tgz", + "integrity": "sha512-X8Z+b/0L4lToKYq+lwnKqi9X/Zek0NibLpsJgVsSxpoYq7JtiCtRb5HqKVEjEw/qAb/4AKKRLOwwKHlWNpm2Eg==", + "dev": true, + "dependencies": { + "readable-stream": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/first-chunk-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==", + "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/first-chunk-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==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "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.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "dev": true + }, + "node_modules/fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", + "dev": true + }, + "node_modules/folder-hash": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/folder-hash/-/folder-hash-4.0.2.tgz", + "integrity": "sha512-Iw9GCqdA+zHfDVvk90TSAV66jq0IwiZaPvPgUiW+DHRwnaPOeZomzlgutx9QclinsQGz/XcVIGlDEJbFhCV5wA==", + "dev": true, + "dependencies": { + "debug": "^4.3.3", + "graceful-fs": "~4.2.9", + "minimatch": "~5.0.0" + }, + "bin": { + "folder-hash": "bin/folder-hash" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/folder-hash/node_modules/minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", + "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "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/fraction.js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", + "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", + "dev": true, + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://www.patreon.com/infusion" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fs-extra": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-3.0.1.tgz", + "integrity": "sha512-V3Z3WZWVUYd8hoCL5xfXJCaHWYzmtwW5XWYSlLgERi8PWd8bx1kUHUk8L1BT57e49oKnDDD180mjfrHc1yA9rg==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^3.0.0", + "universalify": "^0.1.0" + } + }, + "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": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "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/function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gauge": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", + "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "dev": true, + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/generator-jhipster": { + "version": "7.9.3", + "resolved": "https://registry.npmjs.org/generator-jhipster/-/generator-jhipster-7.9.3.tgz", + "integrity": "sha512-iuhXUA0jHygv9wg3cCK+1xdNchi8x3z56TtTPqIeiDP0VNHL2ddesicL4QwNnHzMafk+J1po7hDEGQqURDUpvA==", + "dev": true, + "dependencies": { + "@faker-js/faker": "5.5.3", + "aws-sdk": "2.1204.0", + "axios": "0.27.2", + "chalk": "4.1.2", + "chevrotain": "10.3.0", + "commander": "9.4.0", + "conf": "10.2.0", + "debug": "4.3.4", + "didyoumean": "1.2.2", + "ejs": "3.1.8", + "escape-string-regexp": "4.0.0", + "glob": "8.0.3", + "insight": "0.11.1", + "isbinaryfile": "4.0.10", + "js-yaml": "4.1.0", + "lodash": "4.17.21", + "mem-fs-editor": "9.5.0", + "minimatch": "5.1.0", + "normalize-path": "3.0.0", + "os-locale": "5.0.0", + "p-queue": "6.6.2", + "p-transform": "1.3.0", + "parse-gitignore": "2.0.0", + "pluralize": "8.0.0", + "prettier": "2.7.1", + "prettier-plugin-java": "1.6.2", + "prettier-plugin-packagejson": "2.2.18", + "progress": "2.0.3", + "randexp": "0.5.3", + "semver": "7.3.7", + "shelljs": "0.8.5", + "simple-git": "3.13.0", + "then-request": "6.0.2", + "uuid": "8.3.2", + "winston": "3.8.1", + "yeoman-environment": "3.10.0", + "yeoman-generator": "5.7.0" + }, + "bin": { + "jhipster": "cli/jhipster.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0", + "npm": ">=6.14.4" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/generator-jhipster" + } + }, + "node_modules/generator-jhipster-es-entity-reindexer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/generator-jhipster-es-entity-reindexer/-/generator-jhipster-es-entity-reindexer-1.0.4.tgz", + "integrity": "sha512-oPa26y8QC1622g0YfDMI/Vcqz5EfECzlHL68S7t8AnxplmLVzqPjyzjOPU0pbfjvSbM7kvlXdOfGT6mNXTJIcw==", + "dev": true, + "dependencies": { + "chalk": "4.1.0", + "generator-jhipster": ">=7.0.0", + "semver": "7.1.1" + } + }, + "node_modules/generator-jhipster-es-entity-reindexer/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/generator-jhipster-es-entity-reindexer/node_modules/chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "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/generator-jhipster-es-entity-reindexer/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/generator-jhipster-es-entity-reindexer/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==", + "dev": true + }, + "node_modules/generator-jhipster-es-entity-reindexer/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/generator-jhipster-es-entity-reindexer/node_modules/semver": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.1.1.tgz", + "integrity": "sha512-WfuG+fl6eh3eZ2qAf6goB7nhiCd7NPXhmyFxigB/TOkQyeLP8w8GsVehvtGNtnNmyboz4TgeK40B1Kbql/8c5A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/generator-jhipster-es-entity-reindexer/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/generator-jhipster/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/generator-jhipster/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==", + "dev": true + }, + "node_modules/generator-jhipster/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/generator-jhipster/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "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/generator-jhipster/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/generator-jhipster/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==", + "dev": true + }, + "node_modules/generator-jhipster/node_modules/commander": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.4.0.tgz", + "integrity": "sha512-sRPT+umqkz90UA8M1yqYfnHlZA7fF6nSphDtxeywPZ49ysjxDQybzk13CL+mXekDRG92skbcqCLVovuCusNmFw==", + "dev": true, + "engines": { + "node": "^12.20.0 || >=14" + } + }, + "node_modules/generator-jhipster/node_modules/detect-indent": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", + "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/generator-jhipster/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==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/generator-jhipster/node_modules/git-hooks-list": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/git-hooks-list/-/git-hooks-list-1.0.3.tgz", + "integrity": "sha512-Y7wLWcrLUXwk2noSka166byGCvhMtDRpgHdzCno1UQv/n/Hegp++a2xBWJL1lJarnKD3SWaljD+0z1ztqxuKyQ==", + "dev": true, + "funding": { + "url": "https://github.com/fisker/git-hooks-list?sponsor=1" + } + }, + "node_modules/generator-jhipster/node_modules/globby": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.0.tgz", + "integrity": "sha512-3LifW9M4joGZasyYPz2A1U74zbC/45fvpXUvO/9KbSa+VV0aGZarWkfdgKyR9sExNP0t0x0ss/UMJpNpcaTspw==", + "dev": true, + "dependencies": { + "@types/glob": "^7.1.1", + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.0.3", + "glob": "^7.1.3", + "ignore": "^5.1.1", + "merge2": "^1.2.3", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/generator-jhipster/node_modules/globby/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/generator-jhipster/node_modules/globby/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/generator-jhipster/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/generator-jhipster/node_modules/java-parser": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/java-parser/-/java-parser-2.0.2.tgz", + "integrity": "sha512-fwv1eDYE4OIAN+XS7cD8aB7UdQyAh3Uz36ydWqemvnDKXEdLbxq7qIbvsjpSvS1NHFR+r81N7AjGcpnamjVxJw==", + "dev": true, + "dependencies": { + "chevrotain": "6.5.0", + "lodash": "4.17.21" + } + }, + "node_modules/generator-jhipster/node_modules/java-parser/node_modules/chevrotain": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-6.5.0.tgz", + "integrity": "sha512-BwqQ/AgmKJ8jcMEjaSnfMybnKMgGTrtDKowfTP3pX4jwVy0kNjRsT/AP6h+wC3+3NC+X8X15VWBnTCQlX+wQFg==", + "dev": true, + "dependencies": { + "regexp-to-ast": "0.4.0" + } + }, + "node_modules/generator-jhipster/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==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/generator-jhipster/node_modules/prettier": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz", + "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/generator-jhipster/node_modules/prettier-plugin-java": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/prettier-plugin-java/-/prettier-plugin-java-1.6.2.tgz", + "integrity": "sha512-oVIUOkx50eb9skdRtEIU7MJUsizQ1ZocgXR1w1o8AnieNGpuPI/2pWnpjtbBm9wUG1dHtBGU8cVIr8h+xgzQIg==", + "dev": true, + "dependencies": { + "java-parser": "2.0.2", + "lodash": "4.17.21", + "prettier": "2.3.1" + } + }, + "node_modules/generator-jhipster/node_modules/prettier-plugin-java/node_modules/prettier": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.1.tgz", + "integrity": "sha512-p+vNbgpLjif/+D+DwAZAbndtRrR0md0MwfmOVN9N+2RgyACMT+7tfaRnT+WDPkqnuVwleyuBIG2XBxKDme3hPA==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/generator-jhipster/node_modules/prettier-plugin-packagejson": { + "version": "2.2.18", + "resolved": "https://registry.npmjs.org/prettier-plugin-packagejson/-/prettier-plugin-packagejson-2.2.18.tgz", + "integrity": "sha512-iBjQ3IY6IayFrQHhXvg+YvKprPUUiIJ04Vr9+EbeQPfwGajznArIqrN33c5bi4JcIvmLHGROIMOm9aYakJj/CA==", + "dev": true, + "dependencies": { + "sort-package-json": "1.57.0" + }, + "peerDependencies": { + "prettier": ">= 1.16.0" + } + }, + "node_modules/generator-jhipster/node_modules/regexp-to-ast": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/regexp-to-ast/-/regexp-to-ast-0.4.0.tgz", + "integrity": "sha512-4qf/7IsIKfSNHQXSwial1IFmfM1Cc/whNBQqRwe0V2stPe7KmN1U0tWQiIx6JiirgSrisjE0eECdNf7Tav1Ntw==", + "dev": true + }, + "node_modules/generator-jhipster/node_modules/sort-package-json": { + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/sort-package-json/-/sort-package-json-1.57.0.tgz", + "integrity": "sha512-FYsjYn2dHTRb41wqnv+uEqCUvBpK3jZcTp9rbz2qDTmel7Pmdtf+i2rLaaPMRZeSVM60V3Se31GyWFpmKs4Q5Q==", + "dev": true, + "dependencies": { + "detect-indent": "^6.0.0", + "detect-newline": "3.1.0", + "git-hooks-list": "1.0.3", + "globby": "10.0.0", + "is-plain-obj": "2.1.0", + "sort-object-keys": "^1.1.3" + }, + "bin": { + "sort-package-json": "cli.js" + } + }, + "node_modules/generator-jhipster/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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==", + "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==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz", + "integrity": "sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + }, + "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": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "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/getos": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/getos/-/getos-3.2.1.tgz", + "integrity": "sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==", + "dev": true, + "dependencies": { + "async": "^3.2.0" + } + }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "dev": true, + "dependencies": { + "assert-plus": "^1.0.0" + } + }, + "node_modules/git-hooks-list": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/git-hooks-list/-/git-hooks-list-3.1.0.tgz", + "integrity": "sha512-LF8VeHeR7v+wAbXqfgRlTSX/1BJR9Q1vEMR8JAz1cEg6GX07+zyj3sAdDvYjj/xnlIfVuGgj4qBei1K3hKH+PA==", + "dev": true, + "funding": { + "url": "https://github.com/fisker/git-hooks-list?sponsor=1" + } + }, + "node_modules/github-username": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/github-username/-/github-username-6.0.0.tgz", + "integrity": "sha512-7TTrRjxblSI5l6adk9zd+cV5d6i1OrJSo3Vr9xdGqFLBQo0mz5P9eIfKCDJ7eekVGGFLbce0qbPSnktXV2BjDQ==", + "dev": true, + "dependencies": { + "@octokit/rest": "^18.0.6" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", + "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "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==", + "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-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-dirs/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/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/globalyzer": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/globalyzer/-/globalyzer-0.1.0.tgz", + "integrity": "sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==", + "dev": true + }, + "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/globrex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", + "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==", + "dev": true + }, + "node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "node_modules/grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "node_modules/grouped-queue": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/grouped-queue/-/grouped-queue-2.0.0.tgz", + "integrity": "sha512-/PiFUa7WIsl48dUeCvhIHnwNmAAzlI/eHoJl0vu3nsFA366JleY7Ff8EVTplZu5kO0MIdZjKTTnzItL61ahbnw==", + "dev": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/growly": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", + "integrity": "sha512-+xGQY0YyAWCnqy7Cd++hc2JqMYzlm0dG30Jd0beaA64sROr8C4nt8Yc9V5Ro3avlSUDTN0ulqP/VBKi1/lLygw==", + "dev": true + }, + "node_modules/gzip-size": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", + "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", + "dev": true, + "dependencies": { + "duplexer": "^0.1.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "deprecated": "this library is no longer supported", + "dev": true, + "dependencies": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/har-validator/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/har-validator/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/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.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "dev": true + }, + "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/hosted-git-info": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-5.1.0.tgz", + "integrity": "sha512-Ek+QmMEqZF8XrbFdwoDjSbm7rT23pCgEMOJmz6GPk/s4yH//RQfNPArhIxbguNxROq/+5lNBwCDHMhA903Kx1Q==", + "dev": true, + "dependencies": { + "lru-cache": "^7.5.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", + "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": "3.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", + "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", + "dev": true, + "dependencies": { + "whatwg-encoding": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/html-entities": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", + "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==", + "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-basic": { + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/http-basic/-/http-basic-8.1.3.tgz", + "integrity": "sha512-/EcDMwJZh3mABI2NhGfHOGOeOZITqfkEO4p/xK+l3NpyncIHUQBoMvCSF/b5GqvKtySC2srL/GGG3+EtlqlmCw==", + "dev": true, + "dependencies": { + "caseless": "^0.12.0", + "concat-stream": "^1.6.2", + "http-response-object": "^3.0.1", + "parse-cache-control": "^1.0.1" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", + "dev": true + }, + "node_modules/http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", + "dev": true + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-errors/node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-errors/node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-parser-js": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", + "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": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/http-proxy-middleware": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", + "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", + "dev": true, + "dependencies": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "@types/express": "^4.17.13" + }, + "peerDependenciesMeta": { + "@types/express": { + "optional": true + } + } + }, + "node_modules/http-proxy-middleware/node_modules/is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/http-response-object": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/http-response-object/-/http-response-object-3.0.2.tgz", + "integrity": "sha512-bqX0XTF6fnXSQcEJ2Iuyr75yVakyjIDCqroJQ/aHfSdlM743Cwqoi2nDYMzLGWUcuTWGWy8AAvOKXTfiv6q9RA==", + "dev": true, + "dependencies": { + "@types/node": "^10.0.3" + } + }, + "node_modules/http-response-object/node_modules/@types/node": { + "version": "10.17.60", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz", + "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==", + "dev": true + }, + "node_modules/http-signature": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz", + "integrity": "sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==", + "dev": true, + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^2.0.2", + "sshpk": "^1.14.1" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true, + "engines": { + "node": ">=8.12.0" + } + }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "dev": true, + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/husky": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz", + "integrity": "sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==", + "dev": true, + "bin": { + "husky": "lib/bin.js" + }, + "engines": { + "node": ">=14" + }, + "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==", + "dev": true, + "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-walk": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-5.0.1.tgz", + "integrity": "sha512-yemi4pMf51WKT7khInJqAvsIGzoqYXblnsz0ql8tM+yi1EKYTY1evX4NAbJrLL/Aanr2HyZeluqU+Oi7MGHokw==", + "dev": true, + "dependencies": { + "minimatch": "^5.0.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==", + "dev": true, + "optional": true, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/immutable": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz", + "integrity": "sha512-15gZoQ38eYjEjxkorfbcgBKBL6R7T459OuK+CpcWt7O3KF4uPCx2tD0uFETlUDIyo+1789crbMhTvQBSR5yBMg==", + "dev": true, + "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-fresh/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/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": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "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/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": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "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": "3.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-3.0.0.tgz", + "integrity": "sha512-TxYQaeNW/N8ymDvwAxPyRbhMBtnEwuvaTYpOQkFx1nSeusgezHniEc/l35Vo4iCq/mMiTJbpD7oYxN98hFlfmw==", + "dev": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/inquirer": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.4.tgz", + "integrity": "sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg==", + "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.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/inquirer/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/inquirer/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "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/inquirer/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/inquirer/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==", + "dev": true + }, + "node_modules/inquirer/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/inquirer/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/insight": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/insight/-/insight-0.11.1.tgz", + "integrity": "sha512-TBcZ0qC9dgdmcxL93OoqkY/RZXJtIi0i07phX/QyYk2ysmJtZex59dgTj4Doq50N9CG9dLRe/RIudc/5CCoFNw==", + "dev": true, + "dependencies": { + "async": "^2.6.2", + "chalk": "^4.1.1", + "conf": "^10.0.1", + "inquirer": "^6.3.1", + "lodash.debounce": "^4.0.8", + "os-name": "^4.0.1", + "request": "^2.88.0", + "tough-cookie": "^4.0.0", + "uuid": "^8.3.2" + }, + "engines": { + "node": ">=12.20" + } + }, + "node_modules/insight/node_modules/ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/insight/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/insight/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/insight/node_modules/async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "dev": true, + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/insight/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "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/insight/node_modules/cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==", + "dev": true, + "dependencies": { + "restore-cursor": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/insight/node_modules/cli-width": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", + "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==", + "dev": true + }, + "node_modules/insight/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/insight/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==", + "dev": true + }, + "node_modules/insight/node_modules/figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==", + "dev": true, + "dependencies": { + "escape-string-regexp": "^1.0.5" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/insight/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/insight/node_modules/inquirer": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.2.tgz", + "integrity": "sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ==", + "dev": true, + "dependencies": { + "ansi-escapes": "^3.2.0", + "chalk": "^2.4.2", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.3", + "figures": "^2.0.0", + "lodash": "^4.17.12", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rxjs": "^6.4.0", + "string-width": "^2.1.0", + "strip-ansi": "^5.1.0", + "through": "^2.3.6" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/insight/node_modules/inquirer/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/insight/node_modules/inquirer/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/insight/node_modules/inquirer/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/insight/node_modules/inquirer/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/insight/node_modules/inquirer/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/insight/node_modules/inquirer/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/insight/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": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/insight/node_modules/mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/insight/node_modules/mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha512-r65nCZhrbXXb6dXOACihYApHw2Q6pV0M3V0PSxd74N0+D8nzAdEAITq2oAjA1jVnKI+tGvEBUpqiMh0+rW6zDQ==", + "dev": true + }, + "node_modules/insight/node_modules/onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==", + "dev": true, + "dependencies": { + "mimic-fn": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/insight/node_modules/restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==", + "dev": true, + "dependencies": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/insight/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/insight/node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/insight/node_modules/string-width/node_modules/ansi-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/insight/node_modules/string-width/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "dev": true, + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/insight/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/insight/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/insight/node_modules/tough-cookie": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.2.tgz", + "integrity": "sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==", + "dev": true, + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/insight/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/insight/node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "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/interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/invert-kv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-3.0.1.tgz", + "integrity": "sha512-CYdFeFexxhv/Bcny+Q0BfOV+ltRlJcd4BBZBYFX/O0u4npJrgZtIcjokegtiSMAvlMTJ+Koq0GBCc//3bueQxw==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sindresorhus/invert-kv?sponsor=1" + } + }, + "node_modules/ip": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", + "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==", + "dev": true + }, + "node_modules/ipaddr.js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", + "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "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": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "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==", + "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-callable": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.5.tgz", + "integrity": "sha512-ZIWRujF6MvYGkEuHMYtFRkL2wAtFw89EHfKlXrkPkjQZZRWeh9L1q3SV13NIfHnqxugjLvAOkEHx9mb1zcMnEw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-ci": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", + "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", + "dev": true, + "dependencies": { + "ci-info": "^3.2.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/is-core-module": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", + "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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-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-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "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-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "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-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", + "dev": true + }, + "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-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-like": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/is-number-like/-/is-number-like-1.0.8.tgz", + "integrity": "sha512-6rZi3ezCyFcn5L71ywzz2bS5b2Igl1En3eTlZlvKjpz1n3IZLAYMbKYAIQgFmEu0GENg92ziU/faEOA/aixjbA==", + "dev": true, + "dependencies": { + "lodash.isfinite": "^3.3.2" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "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-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-scoped": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-scoped/-/is-scoped-2.1.0.tgz", + "integrity": "sha512-Cv4OpPTHAK9kHYzkzCrof3VJh7H/PrG2MBUMvvJebaaUMbqhm0YAtXnvh0I3Hnj2tMZWwrRROWLSgfJrKqWmlQ==", + "dev": true, + "dependencies": { + "scoped-regex": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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-typed-array": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.9.tgz", + "integrity": "sha512-kfrlnTTn8pZkfpJMUgYD7YZ3qzeJgWUn8XfVYBARc4wnmNOmLbmuuaAs3q5fvB0UJOn6yHAKaGTPM7d6ezoD/A==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-abstract": "^1.20.0", + "for-each": "^0.3.3", + "has-tostringtag": "^1.0.0" + }, + "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": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "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-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==", + "dev": true + }, + "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-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/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/isbinaryfile": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", + "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", + "dev": true, + "engines": { + "node": ">= 8.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", + "dev": true + }, + "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": "5.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.0.tgz", + "integrity": "sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A==", + "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/istanbul-lib-instrument/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "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-report/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.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.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", + "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "dev": true, + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jake": { + "version": "10.8.5", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz", + "integrity": "sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==", + "dev": true, + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.1", + "minimatch": "^3.0.4" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jake/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jake/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/jake/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "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/jake/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jake/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==", + "dev": true + }, + "node_modules/jake/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jake/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/jake/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/java-parser": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/java-parser/-/java-parser-2.0.4.tgz", + "integrity": "sha512-8dzy+3lJdSakeXgYArzqJCTy2PuH4iW1WjbhPsDaiYDXPeeroksy0s1/TWoc9Zw8zWzS3x8soanHh0AThwwqrQ==", + "dev": true, + "dependencies": { + "chevrotain": "6.5.0", + "lodash": "4.17.21" + } + }, + "node_modules/java-parser/node_modules/chevrotain": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-6.5.0.tgz", + "integrity": "sha512-BwqQ/AgmKJ8jcMEjaSnfMybnKMgGTrtDKowfTP3pX4jwVy0kNjRsT/AP6h+wC3+3NC+X8X15VWBnTCQlX+wQFg==", + "dev": true, + "dependencies": { + "regexp-to-ast": "0.4.0" + } + }, + "node_modules/java-parser/node_modules/regexp-to-ast": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/regexp-to-ast/-/regexp-to-ast-0.4.0.tgz", + "integrity": "sha512-4qf/7IsIKfSNHQXSwial1IFmfM1Cc/whNBQqRwe0V2stPe7KmN1U0tWQiIx6JiirgSrisjE0eECdNf7Tav1Ntw==", + "dev": true + }, + "node_modules/jest": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest/-/jest-28.1.3.tgz", + "integrity": "sha512-N4GT5on8UkZgH0O5LUavMRV1EDEhNTL0KEfRmDIeZHSV7p2XgLoY9t9VDUgL6o+yfdgYHVxuz81G8oB9VG5uyA==", + "dev": true, + "dependencies": { + "@jest/core": "^28.1.3", + "@jest/types": "^28.1.3", + "import-local": "^3.0.2", + "jest-cli": "^28.1.3" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.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": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-28.1.3.tgz", + "integrity": "sha512-esaOfUWJXk2nfZt9SPyC8gA1kNfdKLkQWyzsMlqq8msYSlNKfmZxfRgZn4Cd4MGVUF+7v6dBs0d5TOAKa7iIiA==", + "dev": true, + "dependencies": { + "execa": "^5.0.0", + "p-limit": "^3.1.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-changed-files/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/jest-changed-files/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/jest-changed-files/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/jest-circus": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-28.1.3.tgz", + "integrity": "sha512-cZ+eS5zc79MBwt+IhQhiEp0OeBddpc1n8MBo1nMB8A7oPMKEO+Sre+wHaLJexQUj9Ya/8NOBY0RESUgYjB6fow==", + "dev": true, + "dependencies": { + "@jest/environment": "^28.1.3", + "@jest/expect": "^28.1.3", + "@jest/test-result": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^0.7.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^28.1.3", + "jest-matcher-utils": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-runtime": "^28.1.3", + "jest-snapshot": "^28.1.3", + "jest-util": "^28.1.3", + "p-limit": "^3.1.0", + "pretty-format": "^28.1.3", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-circus/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-circus/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "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/jest-circus/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-circus/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==", + "dev": true + }, + "node_modules/jest-circus/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-circus/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-cli": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-28.1.3.tgz", + "integrity": "sha512-roY3kvrv57Azn1yPgdTebPAXvdR2xfezaKKYzVxZ6It/5NCxzJym6tUI5P1zkdWhfUYkxEI9uZWcQdaFLo8mJQ==", + "dev": true, + "dependencies": { + "@jest/core": "^28.1.3", + "@jest/test-result": "^28.1.3", + "@jest/types": "^28.1.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "import-local": "^3.0.2", + "jest-config": "^28.1.3", + "jest-util": "^28.1.3", + "jest-validate": "^28.1.3", + "prompts": "^2.0.1", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.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/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-cli/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "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/jest-cli/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-cli/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==", + "dev": true + }, + "node_modules/jest-cli/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-cli/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-config": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-28.1.3.tgz", + "integrity": "sha512-MG3INjByJ0J4AsNBm7T3hsuxKQqFIiRo/AUqb1q9LRKI5UU6Aar9JHbr9Ivn1TVwfUD9KirRoM/T6u8XlcQPHQ==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^28.1.3", + "@jest/types": "^28.1.3", + "babel-jest": "^28.1.3", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^28.1.3", + "jest-environment-node": "^28.1.3", + "jest-get-type": "^28.0.2", + "jest-regex-util": "^28.0.2", + "jest-resolve": "^28.1.3", + "jest-runner": "^28.1.3", + "jest-util": "^28.1.3", + "jest-validate": "^28.1.3", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^28.1.3", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/jest-config/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-config/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/jest-config/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "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/jest-config/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-config/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==", + "dev": true + }, + "node_modules/jest-config/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-config/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-config/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/jest-config/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-date-mock": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/jest-date-mock/-/jest-date-mock-1.0.8.tgz", + "integrity": "sha512-0Lyp+z9xvuNmLbK+5N6FOhSiBeux05Lp5bbveFBmYo40Aggl2wwxFoIrZ+rOWC8nDNcLeBoDd2miQdEDSf3iQw==", + "dev": true + }, + "node_modules/jest-diff": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-28.1.3.tgz", + "integrity": "sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "diff-sequences": "^28.1.1", + "jest-get-type": "^28.0.2", + "pretty-format": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-diff/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-diff/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "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/jest-diff/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-diff/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==", + "dev": true + }, + "node_modules/jest-diff/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-diff/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-docblock": { + "version": "28.1.1", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-28.1.1.tgz", + "integrity": "sha512-3wayBVNiOYx0cwAbl9rwm5kKFP8yHH3d/fkEaL02NPTkDojPtheGB7HZSFY4wzX+DxyrvhXz0KSCVksmCknCuA==", + "dev": true, + "dependencies": { + "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-each": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-28.1.3.tgz", + "integrity": "sha512-arT1z4sg2yABU5uogObVPvSlSMQlDA48owx07BDPAiasW0yYpYHYOo4HHLz9q0BVzDVU4hILFjzJw0So9aCL/g==", + "dev": true, + "dependencies": { + "@jest/types": "^28.1.3", + "chalk": "^4.0.0", + "jest-get-type": "^28.0.2", + "jest-util": "^28.1.3", + "pretty-format": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-each/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-each/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "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/jest-each/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-each/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==", + "dev": true + }, + "node_modules/jest-each/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-each/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-environment-jsdom": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-28.1.3.tgz", + "integrity": "sha512-HnlGUmZRdxfCByd3GM2F100DgQOajUBzEitjGqIREcb45kGjZvRrKUdlaF6escXBdcXNl0OBh+1ZrfeZT3GnAg==", + "dev": true, + "dependencies": { + "@jest/environment": "^28.1.3", + "@jest/fake-timers": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/jsdom": "^16.2.4", + "@types/node": "*", + "jest-mock": "^28.1.3", + "jest-util": "^28.1.3", + "jsdom": "^19.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-environment-node": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-28.1.3.tgz", + "integrity": "sha512-ugP6XOhEpjAEhGYvp5Xj989ns5cB1K6ZdjBYuS30umT4CQEETaxSiPcZ/E1kFktX4GkrcM4qu07IIlDYX1gp+A==", + "dev": true, + "dependencies": { + "@jest/environment": "^28.1.3", + "@jest/fake-timers": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "jest-mock": "^28.1.3", + "jest-util": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "28.0.2", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-28.0.2.tgz", + "integrity": "sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==", + "dev": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-28.1.3.tgz", + "integrity": "sha512-3S+RQWDXccXDKSWnkHa/dPwt+2qwA8CJzR61w3FoYCvoo3Pn8tvGcysmMF0Bj0EX5RYvAI2EIvC57OmotfdtKA==", + "dev": true, + "dependencies": { + "@jest/types": "^28.1.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^28.0.2", + "jest-util": "^28.1.3", + "jest-worker": "^28.1.3", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-junit": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/jest-junit/-/jest-junit-14.0.1.tgz", + "integrity": "sha512-h7/wwzPbllgpQhhVcRzRC76/cc89GlazThoV1fDxcALkf26IIlRsu/AcTG64f4nR2WPE3Cbd+i/sVf+NCUHrWQ==", + "dev": true, + "dependencies": { + "mkdirp": "^1.0.4", + "strip-ansi": "^6.0.1", + "uuid": "^8.3.2", + "xml": "^1.0.1" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/jest-leak-detector": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-28.1.3.tgz", + "integrity": "sha512-WFVJhnQsiKtDEo5lG2mM0v40QWnBM+zMdHHyJs8AWZ7J0QZJS59MsyKeJHWhpBZBH32S48FOVvGyOFT1h0DlqA==", + "dev": true, + "dependencies": { + "jest-get-type": "^28.0.2", + "pretty-format": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-matcher-utils": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz", + "integrity": "sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^28.1.3", + "jest-get-type": "^28.0.2", + "pretty-format": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-matcher-utils/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-matcher-utils/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "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/jest-matcher-utils/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-matcher-utils/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==", + "dev": true + }, + "node_modules/jest-matcher-utils/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-matcher-utils/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-message-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz", + "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^28.1.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^28.1.3", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-message-util/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-message-util/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "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/jest-message-util/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-message-util/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==", + "dev": true + }, + "node_modules/jest-message-util/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-message-util/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-mock": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-28.1.3.tgz", + "integrity": "sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA==", + "dev": true, + "dependencies": { + "@jest/types": "^28.1.3", + "@types/node": "*" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.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": "12.2.2", + "resolved": "https://registry.npmjs.org/jest-preset-angular/-/jest-preset-angular-12.2.2.tgz", + "integrity": "sha512-aj5ZwVW6cGGzZKUn6e/jDwFgQh6FHy1zCCXWOeqFCuM3WODrbdUJ93zKrex18e9K1+PvOcP0e20yKbj3gwhfFg==", + "dev": true, + "dependencies": { + "bs-logger": "^0.2.6", + "esbuild-wasm": ">=0.13.8", + "jest-environment-jsdom": "^28.0.0", + "pretty-format": "^28.0.0", + "ts-jest": "^28.0.0" + }, + "engines": { + "node": "^14.15.0 || >=16.10.0" + }, + "optionalDependencies": { + "esbuild": ">=0.13.8" + }, + "peerDependencies": { + "@angular-devkit/build-angular": ">=0.1102.19 <15.0.0", + "@angular/compiler-cli": ">=11.2.14 <15.0.0", + "@angular/core": ">=11.2.14 <15.0.0", + "@angular/platform-browser-dynamic": ">=11.2.14 <15.0.0", + "jest": "^28.0.0", + "typescript": ">=4.3" + } + }, + "node_modules/jest-regex-util": { + "version": "28.0.2", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz", + "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==", + "dev": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-resolve": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-28.1.3.tgz", + "integrity": "sha512-Z1W3tTjE6QaNI90qo/BJpfnvpxtaFTFw5CDgwpyE/Kz8U/06N1Hjf4ia9quUhCh39qIGWF1ZuxFiBiJQwSEYKQ==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^28.1.3", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^28.1.3", + "jest-validate": "^28.1.3", + "resolve": "^1.20.0", + "resolve.exports": "^1.1.0", + "slash": "^3.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-resolve-dependencies": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-28.1.3.tgz", + "integrity": "sha512-qa0QO2Q0XzQoNPouMbCc7Bvtsem8eQgVPNkwn9LnS+R2n8DaVDPL/U1gngC0LTl1RYXJU0uJa2BMC2DbTfFrHA==", + "dev": true, + "dependencies": { + "jest-regex-util": "^28.0.2", + "jest-snapshot": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-resolve/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-resolve/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "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/jest-resolve/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-resolve/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==", + "dev": true + }, + "node_modules/jest-resolve/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-resolve/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runner": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-28.1.3.tgz", + "integrity": "sha512-GkMw4D/0USd62OVO0oEgjn23TM+YJa2U2Wu5zz9xsQB1MxWKDOlrnykPxnMsN0tnJllfLPinHTka61u0QhaxBA==", + "dev": true, + "dependencies": { + "@jest/console": "^28.1.3", + "@jest/environment": "^28.1.3", + "@jest/test-result": "^28.1.3", + "@jest/transform": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.10.2", + "graceful-fs": "^4.2.9", + "jest-docblock": "^28.1.1", + "jest-environment-node": "^28.1.3", + "jest-haste-map": "^28.1.3", + "jest-leak-detector": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-resolve": "^28.1.3", + "jest-runtime": "^28.1.3", + "jest-util": "^28.1.3", + "jest-watcher": "^28.1.3", + "jest-worker": "^28.1.3", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-runner/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-runner/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "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/jest-runner/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-runner/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==", + "dev": true + }, + "node_modules/jest-runner/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runner/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-runner/node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/jest-runner/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runtime": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-28.1.3.tgz", + "integrity": "sha512-NU+881ScBQQLc1JHG5eJGU7Ui3kLKrmwCPPtYsJtBykixrM2OhVQlpMmFWJjMyDfdkGgBMNjXCGB/ebzsgNGQw==", + "dev": true, + "dependencies": { + "@jest/environment": "^28.1.3", + "@jest/fake-timers": "^28.1.3", + "@jest/globals": "^28.1.3", + "@jest/source-map": "^28.1.2", + "@jest/test-result": "^28.1.3", + "@jest/transform": "^28.1.3", + "@jest/types": "^28.1.3", + "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": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-mock": "^28.1.3", + "jest-regex-util": "^28.0.2", + "jest-resolve": "^28.1.3", + "jest-snapshot": "^28.1.3", + "jest-util": "^28.1.3", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-runtime/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-runtime/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/jest-runtime/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "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/jest-runtime/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-runtime/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==", + "dev": true + }, + "node_modules/jest-runtime/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/jest-runtime/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/jest-runtime/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/jest-runtime/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-runtime/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/jest-runtime/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/jest-runtime/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-snapshot": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-28.1.3.tgz", + "integrity": "sha512-4lzMgtiNlc3DU/8lZfmqxN3AYD6GGLbl+72rdBpXvcV+whX7mDrREzkPdp2RnmfIiWBg1YbuFSkXduF2JcafJg==", + "dev": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^28.1.3", + "@jest/transform": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/babel__traverse": "^7.0.6", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^28.1.3", + "graceful-fs": "^4.2.9", + "jest-diff": "^28.1.3", + "jest-get-type": "^28.0.2", + "jest-haste-map": "^28.1.3", + "jest-matcher-utils": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-util": "^28.1.3", + "natural-compare": "^1.4.0", + "pretty-format": "^28.1.3", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-snapshot/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-snapshot/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "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/jest-snapshot/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-snapshot/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==", + "dev": true + }, + "node_modules/jest-snapshot/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-snapshot/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-sonar": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/jest-sonar/-/jest-sonar-0.2.12.tgz", + "integrity": "sha512-8hZO3rhxVobJkMhyuEgsZxfj0QQucCa+dkXVJ3ckz+Gi180F2ET12GwEGP/j4z1owc08Z2JgIt2Qlgl06+3asQ==", + "dev": true, + "dependencies": { + "entities": "^2.1.0", + "strip-ansi": "^6.0.0" + } + }, + "node_modules/jest-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", + "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", + "dev": true, + "dependencies": { + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-util/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-util/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "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/jest-util/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-util/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==", + "dev": true + }, + "node_modules/jest-util/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-util/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-validate": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-28.1.3.tgz", + "integrity": "sha512-SZbOGBWEsaTxBGCOpsRWlXlvNkvTkY0XxRfh7zYmvd8uL5Qzyg0CHAXiXKROflh801quA6+/DsT4ODDthOC/OA==", + "dev": true, + "dependencies": { + "@jest/types": "^28.1.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^28.0.2", + "leven": "^3.1.0", + "pretty-format": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-validate/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "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-validate/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "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/jest-validate/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-validate/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==", + "dev": true + }, + "node_modules/jest-validate/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-validate/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-watcher": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-28.1.3.tgz", + "integrity": "sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g==", + "dev": true, + "dependencies": { + "@jest/test-result": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.10.2", + "jest-util": "^28.1.3", + "string-length": "^4.0.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-watcher/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/jest-watcher/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "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/jest-watcher/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/jest-watcher/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==", + "dev": true + }, + "node_modules/jest-watcher/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-watcher/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jest-worker": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.3.tgz", + "integrity": "sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==", + "dev": true, + "dependencies": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-worker/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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/jmespath": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", + "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==", + "dev": true, + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/joi": { + "version": "17.9.2", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.9.2.tgz", + "integrity": "sha512-Itk/r+V4Dx0V3c7RLFdRh12IOjySm2/WGPMubBT92cQvRfYZhPM2W0hZlctjj72iES8jsRCwp7S/cRmWBnJ4nw==", + "dev": true, + "dependencies": { + "@hapi/hoek": "^9.0.0", + "@hapi/topo": "^5.0.0", + "@sideway/address": "^4.1.3", + "@sideway/formula": "^3.0.1", + "@sideway/pinpoint": "^2.0.0" + } + }, + "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==" + }, + "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/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", + "dev": true + }, + "node_modules/jsdom": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-19.0.0.tgz", + "integrity": "sha512-RYAyjCbxy/vri/CfnjUWJQQtZ3LKlLnDqj+9XLNnJPgEGeirZs3hllKR20re8LUZ6o1b1X4Jat+Qd26zmP41+A==", + "dev": true, + "dependencies": { + "abab": "^2.0.5", + "acorn": "^8.5.0", + "acorn-globals": "^6.0.0", + "cssom": "^0.5.0", + "cssstyle": "^2.3.0", + "data-urls": "^3.0.1", + "decimal.js": "^10.3.1", + "domexception": "^4.0.0", + "escodegen": "^2.0.0", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy-agent": "^5.0.0", + "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": "^3.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^2.0.0", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^10.0.0", + "ws": "^8.2.3", + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsdom/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==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/jsdom/node_modules/tough-cookie": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.2.tgz", + "integrity": "sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==", + "dev": true, + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsdom/node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } + }, + "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": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "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==", + "dev": true + }, + "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==", + "dev": true + }, + "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": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json-stringify-nice": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/json-stringify-nice/-/json-stringify-nice-1.1.4.tgz", + "integrity": "sha512-5Z5RFW63yxReJ7vANgW6eZFGWaQvnPE3WNmZoOJrSkGju2etKA2L5rrOa1sm877TVTFt57A80BH1bArcmlLfPw==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.1.0.tgz", + "integrity": "sha512-DRf0QjnNeCUds3xTjKlQQ3DpJD51GvDjJfnxUVWg6PZTo2otSm+slzNAxU/35hF8/oJIKoG9slq30JYOsF2azg==", + "dev": true + }, + "node_modules/jsonfile": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-3.0.1.tgz", + "integrity": "sha512-oBko6ZHlubVB5mRFkur5vgYR1UyqX+S6Y/oCfLhqNdcc2fYFlDpIoNc7AfKS1KOGcnNAkvsr0grLck9ANM815w==", + "dev": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "dev": true, + "engines": [ + "node >= 0.2.0" + ] + }, + "node_modules/jsprim": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz", + "integrity": "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + } + }, + "node_modules/just-diff": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/just-diff/-/just-diff-5.1.1.tgz", + "integrity": "sha512-u8HXJ3HlNrTzY7zrYYKjNEfBlyjqhdBkoyTVdjtn7p02RJD5NvR8rIClzeGA7t+UYP1/7eAkWNLU0+P3QrEqKQ==", + "dev": true + }, + "node_modules/just-diff-apply": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/just-diff-apply/-/just-diff-apply-5.4.1.tgz", + "integrity": "sha512-AAV5Jw7tsniWwih8Ly3fXxEZ06y+6p5TwQMsw0dzZ/wPKilzyDgdAnL0Ug4NNIquPUOh1vfFWEHbmXUqM5+o8g==", + "dev": true + }, + "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/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/kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", + "dev": true + }, + "node_modules/lazy-ass": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz", + "integrity": "sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==", + "dev": true, + "engines": { + "node": "> 0.8" + } + }, + "node_modules/lcid": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-3.1.1.tgz", + "integrity": "sha512-M6T051+5QCGLBQb8id3hdvIW8+zeFV2FyBGFS9IEK5H9Wt4MueD4bW1eWikpHgZp+5xR3l5c8pZUkQsIA0BFZg==", + "dev": true, + "dependencies": { + "invert-kv": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/less": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/less/-/less-4.1.3.tgz", + "integrity": "sha512-w16Xk/Ta9Hhyei0Gpz9m7VS8F28nieJaL/VyShID7cYvP6IL5oHeL6p4TXSDJqZE/lNv0oJ2pGVjJsRkfwm5FA==", + "dev": true, + "dependencies": { + "copy-anything": "^2.0.1", + "parse-node-version": "^1.0.1", + "tslib": "^2.3.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": "^3.1.0", + "source-map": "~0.6.0" + } + }, + "node_modules/less-loader": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-11.0.0.tgz", + "integrity": "sha512-9+LOWWjuoectIEx3zrfN83NAGxSUB5pWEabbbidVQVgZhN+wN68pOvuyirVlH1IK4VT1f3TmlyvAnCXh8O5KEw==", + "dev": true, + "dependencies": { + "klona": "^2.0.4" + }, + "engines": { + "node": ">= 14.15.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.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "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/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": "4.0.2", + "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-4.0.2.tgz", + "integrity": "sha512-771TFWFD70G1wLTC4oU2Cw4qvtmNrIw+wRvBtn+okgHl7slJVi7zfNcdmqDL72BojM30VNJ2UHylr1o77U37Jw==", + "dev": true, + "dependencies": { + "webpack-sources": "^3.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + }, + "webpack-sources": { + "optional": true + } + } + }, + "node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/limiter": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", + "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==", + "dev": true + }, + "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": "13.2.2", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-13.2.2.tgz", + "integrity": "sha512-71gSwXKy649VrSU09s10uAT0rWCcY3aewhMaHyl2N84oBk4Xs9HgxvUp3AYu+bNsK4NrOYYxvSgg7FyGJ+jGcA==", + "dev": true, + "dependencies": { + "chalk": "5.2.0", + "cli-truncate": "^3.1.0", + "commander": "^10.0.0", + "debug": "^4.3.4", + "execa": "^7.0.0", + "lilconfig": "2.1.0", + "listr2": "^5.0.7", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-inspect": "^1.12.3", + "pidtree": "^0.6.0", + "string-argv": "^0.3.1", + "yaml": "^2.2.2" + }, + "bin": { + "lint-staged": "bin/lint-staged.js" + }, + "engines": { + "node": "^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/lint-staged" + } + }, + "node_modules/lint-staged/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/lint-staged/node_modules/chalk": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", + "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/lint-staged/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/lint-staged/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==", + "dev": true + }, + "node_modules/lint-staged/node_modules/commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/lint-staged/node_modules/execa": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-7.1.1.tgz", + "integrity": "sha512-wH0eMf/UXckdUYnO21+HDztteVv05rq2GXksxT4fCGeHkBhw1DROXh40wcjMcRqDOWE7iPJ4n3M7e2+YFP+76Q==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.1", + "human-signals": "^4.3.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^3.0.7", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": "^14.18.0 || ^16.14.0 || >=18.0.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/lint-staged/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/lint-staged/node_modules/human-signals": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", + "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", + "dev": true, + "engines": { + "node": ">=14.18.0" + } + }, + "node_modules/lint-staged/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/lint-staged/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/listr2": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-5.0.8.tgz", + "integrity": "sha512-mC73LitKHj9w6v30nLNGPetZIlfpUniNSsxxrbaPcWOjDb92SHPzJPi/t+v1YC/lxKz/AJ9egOjww0qUuFxBpA==", + "dev": true, + "dependencies": { + "cli-truncate": "^2.1.0", + "colorette": "^2.0.19", + "log-update": "^4.0.0", + "p-map": "^4.0.0", + "rfdc": "^1.3.0", + "rxjs": "^7.8.0", + "through": "^2.3.8", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": "^14.13.1 || >=16.0.0" + }, + "peerDependencies": { + "enquirer": ">= 2.3.0 < 3" + }, + "peerDependenciesMeta": { + "enquirer": { + "optional": true + } + } + }, + "node_modules/lint-staged/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/lint-staged/node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/npm-run-path": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", + "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", + "dev": true, + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lint-staged/node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/lint-staged/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/lint-staged/node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/listr2": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.14.0.tgz", + "integrity": "sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g==", + "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.1", + "through": "^2.3.8", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "enquirer": ">= 2.3.0 < 3" + }, + "peerDependenciesMeta": { + "enquirer": { + "optional": true + } + } + }, + "node_modules/listr2/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "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/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/listr2/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==", + "dev": true + }, + "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/load-yaml-file": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/load-yaml-file/-/load-yaml-file-0.2.0.tgz", + "integrity": "sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.5", + "js-yaml": "^3.13.0", + "pify": "^4.0.1", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/load-yaml-file/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/load-yaml-file/node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "dev": true, + "engines": { + "node": ">=6.11.5" + } + }, + "node_modules/loader-utils": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.0.tgz", + "integrity": "sha512-HVl9ZqccQihZ7JM85dco1MvO9G+ONvxoGa9rkhzFsneGLKSUg1gJf9bWzhRhcvm2qChhWpebQhP44qxjKIUCaQ==", + "dev": true, + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/localtunnel": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/localtunnel/-/localtunnel-2.0.2.tgz", + "integrity": "sha512-n418Cn5ynvJd7m/N1d9WVJISLJF/ellZnfsLnx8WBWGzxv/ntNcFkJ1o6se5quUhCplfLGBNL5tYHiq5WF3Nug==", + "dev": true, + "dependencies": { + "axios": "0.21.4", + "debug": "4.3.2", + "openurl": "1.1.1", + "yargs": "17.1.1" + }, + "bin": { + "lt": "bin/lt.js" + }, + "engines": { + "node": ">=8.3.0" + } + }, + "node_modules/localtunnel/node_modules/axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "dev": true, + "dependencies": { + "follow-redirects": "^1.14.0" + } + }, + "node_modules/localtunnel/node_modules/debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/localtunnel/node_modules/yargs": { + "version": "17.1.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.1.1.tgz", + "integrity": "sha512-c2k48R0PwKIqKhPMWjeiF6y2xY/gPMUlro0sgxqXpbOIohWiLNXWslsootttv7E1e73QPAMQSg5FeySbVcpsPQ==", + "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": ">=12" + } + }, + "node_modules/localtunnel/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/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/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + }, + "node_modules/lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "dev": true + }, + "node_modules/lodash.isfinite": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/lodash.isfinite/-/lodash.isfinite-3.3.2.tgz", + "integrity": "sha512-7FGG40uhC8Mm633uKW1r58aElFlBlxCrg9JfSi3P6aYiWmfiWF0PgMd86ZUsxE5GwWPdHoS2+48bwTh2VPkIQA==", + "dev": true + }, + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "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.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "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-symbols/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "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/log-symbols/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/log-symbols/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==", + "dev": true + }, + "node_modules/log-symbols/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/log-symbols/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-update/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/log-update/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==", + "dev": true + }, + "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/logform": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.4.2.tgz", + "integrity": "sha512-W4c9himeAwXEdZ05dQNerhFz2XG80P9Oj0loPUMV23VC2it0orMHQhJm4hdnnor3rd1HsGf6a2lPwBM1zeXHGw==", + "dev": true, + "dependencies": { + "@colors/colors": "1.5.0", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + } + }, + "node_modules/lru-cache": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.14.0.tgz", + "integrity": "sha512-EIRtP1GrSJny0dqb50QXRUNBxHJhcpxHC++M5tD7RYbvLLn5KVWKsbyswSSqDuU15UFi3bgTQIY8nhDMeF6aDQ==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/macos-release": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.5.0.tgz", + "integrity": "sha512-EIgv+QZ9r+814gjJj0Bt5vSLJLzswGmSUbUpbi9AIr/fsN2IWFBl2NucV9PAiek+U1STK468tEkxmVYUtuAN3g==", + "dev": true, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/magic-string": { + "version": "0.26.2", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.2.tgz", + "integrity": "sha512-NzzlXpclt5zAbmo6h6jNc8zl2gNRGHvmsZW4IvZhTC4W7k4OlLP+S5YLussa/r3ixNT66KOQfNORlXHSOy/X4A==", + "dependencies": { + "sourcemap-codec": "^1.4.8" + }, + "engines": { + "node": ">=12" + } + }, + "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.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "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/make-fetch-happen": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz", + "integrity": "sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==", + "dev": true, + "dependencies": { + "agentkeepalive": "^4.2.1", + "cacache": "^16.1.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^2.0.3", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^9.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "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/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mem": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/mem/-/mem-5.1.1.tgz", + "integrity": "sha512-qvwipnozMohxLXG1pOqoLiZKNkC4r4qqRucSoDwXowsNGDSULiqFTRUF05vcZWnwJSG22qTsynQhxbaMtnX9gw==", + "dev": true, + "dependencies": { + "map-age-cleaner": "^0.1.3", + "mimic-fn": "^2.1.0", + "p-is-promise": "^2.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mem-fs": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/mem-fs/-/mem-fs-2.2.1.tgz", + "integrity": "sha512-yiAivd4xFOH/WXlUi6v/nKopBh1QLzwjFi36NK88cGt/PRXI8WeBASqY+YSjIVWvQTx3hR8zHKDBMV6hWmglNA==", + "dev": true, + "dependencies": { + "@types/node": "^15.6.1", + "@types/vinyl": "^2.0.4", + "vinyl": "^2.0.1", + "vinyl-file": "^3.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/mem-fs-editor": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/mem-fs-editor/-/mem-fs-editor-9.5.0.tgz", + "integrity": "sha512-7p+bBDqsSisO20YIZf2ntYvST27fFJINn7CKE21XdPUQDcLV62b/yB5sTOooQeEoiZ3rldZQ+4RfONgL/gbRoA==", + "dev": true, + "dependencies": { + "binaryextensions": "^4.16.0", + "commondir": "^1.0.1", + "deep-extend": "^0.6.0", + "ejs": "^3.1.8", + "globby": "^11.1.0", + "isbinaryfile": "^4.0.8", + "minimatch": "^3.1.2", + "multimatch": "^5.0.0", + "normalize-path": "^3.0.0", + "textextensions": "^5.13.0" + }, + "engines": { + "node": ">=12.10.0" + }, + "peerDependencies": { + "mem-fs": "^2.1.0" + }, + "peerDependenciesMeta": { + "mem-fs": { + "optional": true + } + } + }, + "node_modules/mem-fs-editor/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/mem-fs-editor/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/mem-fs/node_modules/@types/node": { + "version": "15.14.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-15.14.9.tgz", + "integrity": "sha512-qjd88DrCxupx/kJD5yQgZdcYKZKSIGBVDIBE1/LTGcNm3d2Np/jxojkdePDdfnBHJc5W7vSMpbJ1aB7p/Py69A==", + "dev": true + }, + "node_modules/mem/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==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/memfs": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.7.tgz", + "integrity": "sha512-ygaiUSNalBX85388uskeCyhSAoOSgzBbtVCr9jA2RROssFL9Q19/ZXFqS+2Th2sr1ewNIWgFdLzLC3Yl1Zv+lw==", + "dev": true, + "dependencies": { + "fs-monkey": "^1.0.3" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", + "dev": true + }, + "node_modules/merge-jsons-webpack-plugin": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/merge-jsons-webpack-plugin/-/merge-jsons-webpack-plugin-2.0.1.tgz", + "integrity": "sha512-8GP8rpOX3HSFsm7Gx+b3OAQR7yhgeAQvMqcZOJ+/cQIrqdak1c42a2T2vyeee8pzGPBf7pMLumthPh4CHgv2BA==", + "dev": true, + "dependencies": { + "glob": "7.1.1" + } + }, + "node_modules/merge-jsons-webpack-plugin/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/merge-jsons-webpack-plugin/node_modules/glob": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", + "integrity": "sha512-mRyN/EsN2SyNhKWykF3eEGhDpeNplMWaW18Bmh76tnOqk5TbELAVwFAYOCmKVssOYFrYvvLMguiA+NXO3ZTuVA==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.2", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/merge-jsons-webpack-plugin/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/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": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "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==", + "dev": true, + "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==", + "dev": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "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/mini-css-extract-plugin": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.6.1.tgz", + "integrity": "sha512-wd+SD57/K6DiV7jIR34P+s3uckTRuQvx0tKPcvjFlrEylk6P4mQ2KSWk1hblj1Kxaqok7LogKOieygXqBczNlg==", + "dev": true, + "dependencies": { + "schema-utils": "^4.0.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/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.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": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.4.tgz", + "integrity": "sha512-I9WPbWHCGu8W+6k1ZiGpPu0GkoKBeorkfKNuAFBNS1HNFJvke82sxvI5bzcCNpWPorkOO5QQ+zomzzwRxejXiw==", + "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-fetch": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-2.1.2.tgz", + "integrity": "sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==", + "dev": true, + "dependencies": { + "minipass": "^3.1.6", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "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-json-stream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minipass-json-stream/-/minipass-json-stream-1.0.1.tgz", + "integrity": "sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg==", + "dev": true, + "dependencies": { + "jsonparse": "^1.3.1", + "minipass": "^3.0.0" + } + }, + "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/minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "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/mitt": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-1.2.0.tgz", + "integrity": "sha512-r6lj77KlwqLhIUku9UWYes7KJtsczvolZkzp8hbaDPPaE24OmWl5s539Mytlj22siEQKosZ26qCBgda2PKwoJw==", + "dev": true + }, + "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/mkdirp-infer-owner": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mkdirp-infer-owner/-/mkdirp-infer-owner-2.0.0.tgz", + "integrity": "sha512-sdqtiFt3lkOaYvTXSRIUjkIdPTcxgv5+fgqYE/5qgwdw12cOrAuzzgzvVExIkH/ul1oeHN3bCLOWSG3XOqbKKw==", + "dev": true, + "dependencies": { + "chownr": "^2.0.0", + "infer-owner": "^1.0.4", + "mkdirp": "^1.0.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mrmime": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz", + "integrity": "sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==", + "dev": true, + "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/multicast-dns": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", + "dev": true, + "dependencies": { + "dns-packet": "^5.2.2", + "thunky": "^1.0.2" + }, + "bin": { + "multicast-dns": "cli.js" + } + }, + "node_modules/multimatch": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-5.0.0.tgz", + "integrity": "sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA==", + "dev": true, + "dependencies": { + "@types/minimatch": "^3.0.3", + "array-differ": "^3.0.0", + "array-union": "^2.1.0", + "arrify": "^2.0.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/multimatch/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/multimatch/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/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "node_modules/nanoid": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true + }, + "node_modules/needle": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/needle/-/needle-3.1.0.tgz", + "integrity": "sha512-gCE9weDhjVGCRqS8dwDR/D3GTAeyXLXuqp7I8EzH6DllZGXSUyxuqqLh+YX9rMAWaaTFyVAg6rHGL25dqvczKw==", + "dev": true, + "optional": true, + "dependencies": { + "debug": "^3.2.6", + "iconv-lite": "^0.6.3", + "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/needle/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, + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/needle/node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true, + "optional": true + }, + "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/ng2-charts": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/ng2-charts/-/ng2-charts-4.0.0.tgz", + "integrity": "sha512-1COLMs1UH8XIurk9C3pBQW3zH4RM3ggPtaC5vGjEmRGZ2cK/j8DqpzN4xMqyk0KB4D2vw/ZejgXmxxZ4Ie58Rw==", + "dependencies": { + "lodash-es": "^4.17.15", + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/common": ">=14.0.0", + "@angular/core": ">=14.0.0", + "chart.js": "^3.4.0", + "rxjs": "^6.5.3 || ^7.4.0" + } + }, + "node_modules/ngx-infinite-scroll": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/ngx-infinite-scroll/-/ngx-infinite-scroll-14.0.0.tgz", + "integrity": "sha512-YZB5PBPXSERNtCGQRZTVflbgkh5asp01NPfC8KPItemmQik1Ip8ZCCbcyHA77TDTdilmaiu8TbguA3geg/LMWw==", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/common": ">=14.0.0 <15.0.0", + "@angular/core": ">=14.0.0 <15.0.0" + } + }, + "node_modules/ngx-webstorage": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/ngx-webstorage/-/ngx-webstorage-10.0.1.tgz", + "integrity": "sha512-OWmzAzby+/UrbRY/5d229Y4NzFn1a/u2WSEeZqzY5lwB/3d8ODZ6mlW/BZGIuuZ48Hp8tXMM3pFCz9+pEyzvDA==", + "dependencies": { + "tslib": "^2.0.0" + }, + "peerDependencies": { + "@angular/common": "^14.0.0", + "@angular/core": "^14.0.0" + } + }, + "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/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==", + "dev": true, + "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": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true + }, + "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": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true + }, + "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": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "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==", + "dev": true, + "engines": { + "node": ">= 6.13.0" + } + }, + "node_modules/node-gyp": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-9.1.0.tgz", + "integrity": "sha512-HkmN0ZpQJU7FLbJauJTHkHlSVAXlNGDAzH/VYFZGDOnFyn/Na3GlNJfkudmufOdS6/jNFhy88ObzL7ERz9es1g==", + "dev": true, + "dependencies": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^10.0.3", + "nopt": "^5.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^12.22 || ^14.13 || >=16" + } + }, + "node_modules/node-gyp-build": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.5.0.tgz", + "integrity": "sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg==", + "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-gyp/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/node-gyp/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/node-gyp/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/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true + }, + "node_modules/node-notifier": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-9.0.1.tgz", + "integrity": "sha512-fPNFIp2hF/Dq7qLDzSg4vZ0J4e9v60gJR+Qx7RbjbWqzPDdEqeVpEx5CFeDAELIl+A/woaaNn1fQ5nEVerMxJg==", + "dev": true, + "dependencies": { + "growly": "^1.3.0", + "is-wsl": "^2.2.0", + "semver": "^7.3.2", + "shellwords": "^0.1.1", + "uuid": "^8.3.0", + "which": "^2.0.2" + } + }, + "node_modules/node-releases": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==" + }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dev": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/normalize-package-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-4.0.1.tgz", + "integrity": "sha512-EBk5QKKuocMJhB3BILuKhmaPjI8vNRSpIfO9woLC6NyHVkKKdVEdAO1mrT0ZfxNR1lKwCcTkuZfmGIFdizZ8Pg==", + "dev": true, + "dependencies": { + "hosted-git-info": "^5.0.0", + "is-core-module": "^2.8.1", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "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==", + "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": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-bundled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz", + "integrity": "sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ==", + "dev": true, + "dependencies": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "node_modules/npm-install-checks": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-5.0.0.tgz", + "integrity": "sha512-65lUsMI8ztHCxFz5ckCEC44DRvEGdZX5usQFriauxHEwt7upv1FKaQEmAtU0YnOAdwuNWCmk64xYiQABNrEyLA==", + "dev": true, + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-normalize-package-bin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", + "dev": true + }, + "node_modules/npm-package-arg": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-9.1.0.tgz", + "integrity": "sha512-4J0GL+u2Nh6OnhvUKXRr2ZMG4lR8qtLp+kv7UiV00Y+nGiSxtttCyIRHCt5L5BNkXQld/RceYItau3MDOoGiBw==", + "dev": true, + "dependencies": { + "hosted-git-info": "^5.0.0", + "proc-log": "^2.0.1", + "semver": "^7.3.5", + "validate-npm-package-name": "^4.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-packlist": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-5.1.3.tgz", + "integrity": "sha512-263/0NGrn32YFYi4J533qzrQ/krmmrWwhKkzwTuM4f/07ug51odoaNjUexxO4vxlzURHcmYMH1QjvHjsNDKLVg==", + "dev": true, + "dependencies": { + "glob": "^8.0.1", + "ignore-walk": "^5.0.1", + "npm-bundled": "^2.0.0", + "npm-normalize-package-bin": "^2.0.0" + }, + "bin": { + "npm-packlist": "bin/index.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-packlist/node_modules/npm-bundled": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-2.0.1.tgz", + "integrity": "sha512-gZLxXdjEzE/+mOstGDqR6b0EkhJ+kM6fxM6vUuckuctuVPh80Q6pw/rSZj9s4Gex9GxWtIicO1pc8DB9KZWudw==", + "dev": true, + "dependencies": { + "npm-normalize-package-bin": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-packlist/node_modules/npm-normalize-package-bin": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-2.0.0.tgz", + "integrity": "sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ==", + "dev": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-pick-manifest": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-7.0.1.tgz", + "integrity": "sha512-IA8+tuv8KujbsbLQvselW2XQgmXWS47t3CB0ZrzsRZ82DbDfkcFunOaPm4X7qNuhMfq+FmV7hQT4iFVpHqV7mg==", + "dev": true, + "dependencies": { + "npm-install-checks": "^5.0.0", + "npm-normalize-package-bin": "^1.0.1", + "npm-package-arg": "^9.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/npm-registry-fetch": { + "version": "13.3.1", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-13.3.1.tgz", + "integrity": "sha512-eukJPi++DKRTjSBRcDZSDDsGqRK3ehbxfFUcgaRd0Yp6kRwOwh2WVn0r+8rMB4nnuzvAk6rQVzl6K5CkYOmnvw==", + "dev": true, + "dependencies": { + "make-fetch-happen": "^10.0.6", + "minipass": "^3.1.6", + "minipass-fetch": "^2.0.3", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.1.2", + "npm-package-arg": "^9.0.1", + "proc-log": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "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/npmlog": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", + "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", + "dev": true, + "dependencies": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/nwsapi": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.2.tgz", + "integrity": "sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw==", + "dev": true + }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "dev": true, + "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==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.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": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "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": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "dev": true, + "dependencies": { + "fn.name": "1.x.x" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "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==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/open": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", + "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", + "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/opener": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", + "dev": true, + "bin": { + "opener": "bin/opener-bin.js" + } + }, + "node_modules/openurl": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/openurl/-/openurl-1.1.1.tgz", + "integrity": "sha512-d/gTkTb1i1GKz5k3XE3XFV/PxQ1k45zDqGP2OA7YhgsaLoqm6qRvARAZOFer1fcXritWlGBRCu/UgeS4HAnXAA==", + "dev": true + }, + "node_modules/opn": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.3.0.tgz", + "integrity": "sha512-bYJHo/LOmoTd+pfiYhfZDnf9zekVJrY+cnS2a5F2x+w5ppvTqObojTP7WiFG+kVZs9Inw+qQ/lw7TroWwhdd2g==", + "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": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==", + "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/ora/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ora/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "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/ora/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/ora/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==", + "dev": true + }, + "node_modules/ora/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ora/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/os-locale": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-5.0.0.tgz", + "integrity": "sha512-tqZcNEDAIZKBEPnHPlVDvKrp7NzgLi7jRmhKiUoa2NUmhl13FtkAGLUVR+ZsYvApBQdBfYm43A4tXXQ4IrYLBA==", + "dev": true, + "dependencies": { + "execa": "^4.0.0", + "lcid": "^3.0.0", + "mem": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/os-name": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/os-name/-/os-name-4.0.1.tgz", + "integrity": "sha512-xl9MAoU97MH1Xt5K9ERft2YfCAoaO6msy1OBA0ozxEC0x0TmIoE6K3QvgJMMZA9yKGLmHXNY/YZoDbiGDj4zYw==", + "dev": true, + "dependencies": { + "macos-release": "^2.5.0", + "windows-release": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ospath": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz", + "integrity": "sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==", + "dev": true + }, + "node_modules/p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha512-wB3wfAxZpk2AzOfUMJNL+d36xothRSyj8EXOa4f6GMqYDN9BJaaSISbsk+wS9abmnebVw95C2Kb5t85UmpCxuw==", + "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": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", + "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "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": "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/p-locate/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/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-queue": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz", + "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==", + "dev": true, + "dependencies": { + "eventemitter3": "^4.0.4", + "p-timeout": "^3.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "dev": true, + "dependencies": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-retry/node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "dev": true, + "dependencies": { + "p-finally": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-transform": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-transform/-/p-transform-1.3.0.tgz", + "integrity": "sha512-UJKdSzgd3KOnXXAtqN5+/eeHcvTn1hBkesEmElVgvO/NAYcxAvmjzIGmnNd3Tb/gRAvMBdNRFD4qAWdHxY6QXg==", + "dev": true, + "dependencies": { + "debug": "^4.3.2", + "p-queue": "^6.6.2" + }, + "engines": { + "node": ">=12.10.0" + } + }, + "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==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/pacote": { + "version": "13.6.2", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-13.6.2.tgz", + "integrity": "sha512-Gu8fU3GsvOPkak2CkbojR7vjs3k3P9cA6uazKTHdsdV0gpCEQq2opelnEv30KRQWgVzP5Vd/5umjcedma3MKtg==", + "dev": true, + "dependencies": { + "@npmcli/git": "^3.0.0", + "@npmcli/installed-package-contents": "^1.0.7", + "@npmcli/promise-spawn": "^3.0.0", + "@npmcli/run-script": "^4.1.0", + "cacache": "^16.0.0", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "infer-owner": "^1.0.4", + "minipass": "^3.1.6", + "mkdirp": "^1.0.4", + "npm-package-arg": "^9.0.0", + "npm-packlist": "^5.1.0", + "npm-pick-manifest": "^7.0.0", + "npm-registry-fetch": "^13.0.1", + "proc-log": "^2.0.0", + "promise-retry": "^2.0.1", + "read-package-json": "^5.0.0", + "read-package-json-fast": "^2.0.3", + "rimraf": "^3.0.2", + "ssri": "^9.0.0", + "tar": "^6.1.11" + }, + "bin": { + "pacote": "lib/bin.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "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/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-cache-control": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-cache-control/-/parse-cache-control-1.0.1.tgz", + "integrity": "sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg==", + "dev": true + }, + "node_modules/parse-conflict-json": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/parse-conflict-json/-/parse-conflict-json-2.0.2.tgz", + "integrity": "sha512-jDbRGb00TAPFsKWCpZZOT93SxVP9nONOSgES3AevqRq/CHvavEBvKAjxX9p5Y5F0RZLxH9Ufd9+RwtCsa+lFDA==", + "dev": true, + "dependencies": { + "json-parse-even-better-errors": "^2.3.1", + "just-diff": "^5.0.1", + "just-diff-apply": "^5.2.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/parse-gitignore": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parse-gitignore/-/parse-gitignore-2.0.0.tgz", + "integrity": "sha512-RmVuCHWsfu0QPNW+mraxh/xjQVw/lhUCUru8Zni3Ctq3AoMhpDTq0OVdKS6iesd6Kqb7viCV3isAL43dciOSog==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "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/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/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", + "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": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true + }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", + "dev": true + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "dev": true, + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "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.2.0", + "resolved": "https://registry.npmjs.org/piscina/-/piscina-3.2.0.tgz", + "integrity": "sha512-yn/jMdHRw+q2ZJhFhyqsmANcbF6V2QwmD84c6xRau+QpQOmtrBCoRGdvTfeuFDYXB5W2m6MfLkjkvQa9lUSmIA==", + "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-up": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", + "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/pkg-up/node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/portscanner": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/portscanner/-/portscanner-2.2.0.tgz", + "integrity": "sha512-IFroCz/59Lqa2uBvzK3bKDbDDIEaAY8XJ1jFxcLWTqosrsc32//P4VuSB2vZXoHiHqOmx8B5L5hnKOxL/7FlPw==", + "dev": true, + "dependencies": { + "async": "^2.6.0", + "is-number-like": "^1.0.3" + }, + "engines": { + "node": ">=0.4", + "npm": ">=1.0.0" + } + }, + "node_modules/portscanner/node_modules/async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "dev": true, + "dependencies": { + "lodash": "^4.17.14" + } + }, + "node_modules/positioning": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/positioning/-/positioning-2.0.1.tgz", + "integrity": "sha512-DsAgM42kV/ObuwlRpAzDTjH9E8fGKkMDJHWFX+kfNXSxh7UCCQxEmdjv/Ws5Ft1XDnt3JT8fIDYeKNSE2TbttA==" + }, + "node_modules/postcss": { + "version": "8.4.16", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.16.tgz", + "integrity": "sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + } + ], + "dependencies": { + "nanoid": "^3.3.4", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-attribute-case-insensitive": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.2.tgz", + "integrity": "sha512-XIidXV8fDr0kKt28vqki84fRK8VW8eTuIa4PChv2MqKuT6C9UjmSKzen6KaWhWEoYvwxFCa7n/tC1SZ3tyq4SQ==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-clamp": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-clamp/-/postcss-clamp-4.1.0.tgz", + "integrity": "sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": ">=7.6.0" + }, + "peerDependencies": { + "postcss": "^8.4.6" + } + }, + "node_modules/postcss-color-functional-notation": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-4.2.4.tgz", + "integrity": "sha512-2yrTAUZUab9s6CpxkxC4rVgFEVaR6/2Pipvi6qcgvnYiVqZcbDHEoBDhrXzyb7Efh2CCfHQNtcqWcIruDTIUeg==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-color-hex-alpha": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-8.0.4.tgz", + "integrity": "sha512-nLo2DCRC9eE4w2JmuKgVA3fGL3d01kGq752pVALF68qpGLmx2Qrk91QTKkdUqqp45T1K1XV8IhQpcu1hoAQflQ==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-color-rebeccapurple": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-7.1.1.tgz", + "integrity": "sha512-pGxkuVEInwLHgkNxUc4sdg4g3py7zUeCQ9sMfwyHAT+Ezk8a4OaaVZ8lIY5+oNqA/BXXgLyXv0+5wHP68R79hg==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-custom-media": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-8.0.2.tgz", + "integrity": "sha512-7yi25vDAoHAkbhAzX9dHx2yc6ntS4jQvejrNcC+csQJAXjj15e7VcWfMgLqBNAbOvqi5uIa9huOVwdHbf+sKqg==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.3" + } + }, + "node_modules/postcss-custom-properties": { + "version": "12.1.8", + "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.8.tgz", + "integrity": "sha512-8rbj8kVu00RQh2fQF81oBqtduiANu4MIxhyf0HbbStgPtnFlWn0yiaYTpLHrPnJbffVY1s9apWsIoVZcc68FxA==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-custom-selectors": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-6.0.3.tgz", + "integrity": "sha512-fgVkmyiWDwmD3JbpCmB45SvvlCD6z9CG6Ie6Iere22W5aHea6oWa7EM2bpnv2Fj3I94L3VbtvX9KqwSi5aFzSg==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.4" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.3" + } + }, + "node_modules/postcss-dir-pseudo-class": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-6.0.5.tgz", + "integrity": "sha512-eqn4m70P031PF7ZQIvSgy9RSJ5uI2171O/OO/zcRNYpJbvaeKFUlar1aJ7rmgiQtbm0FSPsRewjpdS0Oew7MPA==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-double-position-gradients": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-3.1.2.tgz", + "integrity": "sha512-GX+FuE/uBR6eskOK+4vkXgT6pDkexLokPaz/AbJna9s5Kzp/yl488pKPjhy0obB475ovfT1Wv8ho7U/cHNaRgQ==", + "dev": true, + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-env-function": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-4.0.6.tgz", + "integrity": "sha512-kpA6FsLra+NqcFnL81TnsU+Z7orGtDTxcOhl6pwXeEq1yFPpRMkCDpHhrz8CFQDr/Wfm0jLiNQ1OsGGPjlqPwA==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-focus-visible": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-6.0.4.tgz", + "integrity": "sha512-QcKuUU/dgNsstIK6HELFRT5Y3lbrMLEOwG+A4s5cA+fx3A3y/JTq3X9LaOj3OC3ALH0XqyrgQIgey/MIZ8Wczw==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-focus-within": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-5.0.4.tgz", + "integrity": "sha512-vvjDN++C0mu8jz4af5d52CB184ogg/sSxAFS+oUJQq2SuCe7T5U2iIsVJtsCp2d6R4j0jr5+q3rPkBVZkXD9fQ==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.9" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-font-variant": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", + "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", + "dev": true, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/postcss-gap-properties": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.5.tgz", + "integrity": "sha512-IuE6gKSdoUNcvkGIqdtjtcMtZIFyXZhmFd5RUlg97iVEvp1BZKV5ngsAjCjrVy+14uhGBQl9tzmi1Qwq4kqVOg==", + "dev": true, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-image-set-function": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-4.0.7.tgz", + "integrity": "sha512-9T2r9rsvYzm5ndsBE8WgtrMlIT7VbtTfE7b3BQnudUqnBcBo7L758oc+o+pdj/dUV0l5wjwSdjeOH2DZtfv8qw==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-import": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.1.0.tgz", + "integrity": "sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==", + "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": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz", + "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==", + "dev": true, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-lab-function": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-4.2.1.tgz", + "integrity": "sha512-xuXll4isR03CrQsmxyz92LJB2xX9n+pZJ5jE9JgcnmsCammLyKdlzrBin+25dy6wIjfhJpKBAN80gsTlCgRk2w==", + "dev": true, + "dependencies": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-loader": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.0.1.tgz", + "integrity": "sha512-VRviFEyYlLjctSM93gAZtcJJ/iSkPZ79zWbN/1fSH+NisBByEiVLqpdVDrPLVSi8DX0oJo12kL/GppTBdKVXiQ==", + "dev": true, + "dependencies": { + "cosmiconfig": "^7.0.0", + "klona": "^2.0.5", + "semver": "^7.3.7" + }, + "engines": { + "node": ">= 14.15.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": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.4.tgz", + "integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==", + "dev": true, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "peerDependencies": { + "postcss": "^8.4" + } + }, + "node_modules/postcss-media-minmax": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz", + "integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "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": "10.1.10", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-10.1.10.tgz", + "integrity": "sha512-lqd7LXCq0gWc0wKXtoKDru5wEUNjm3OryLVNRZ8OnW8km6fSNUuFrjEhU3nklxXE2jvd4qrox566acgh+xQt8w==", + "dev": true, + "dependencies": { + "@csstools/selector-specificity": "^2.0.0", + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-opacity-percentage": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/postcss-opacity-percentage/-/postcss-opacity-percentage-1.1.2.tgz", + "integrity": "sha512-lyUfF7miG+yewZ8EAk9XUBIlrHyUE6fijnesuz+Mj5zrIHIEw6KcIZSOk/elVMqzLvREmXB83Zi/5QpNRYd47w==", + "dev": true, + "funding": [ + { + "type": "kofi", + "url": "https://ko-fi.com/mrcgrtz" + }, + { + "type": "liberapay", + "url": "https://liberapay.com/mrcgrtz" + } + ], + "engines": { + "node": "^12 || ^14 || >=16" + } + }, + "node_modules/postcss-overflow-shorthand": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.4.tgz", + "integrity": "sha512-otYl/ylHK8Y9bcBnPLo3foYFLL6a6Ak+3EQBPOTR7luMYCOsiVTUk1iLvNf6tVPNGXcoL9Hoz37kpfriRIFb4A==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-page-break": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", + "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", + "dev": true, + "peerDependencies": { + "postcss": "^8" + } + }, + "node_modules/postcss-place": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-7.0.5.tgz", + "integrity": "sha512-wR8igaZROA6Z4pv0d+bvVrvGY4GVHihBCBQieXFY3kuSuMyOmEnnfFzHl/tQuqHZkfkIVBEbDvYcFfHmpSet9g==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-preset-env": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-7.8.0.tgz", + "integrity": "sha512-leqiqLOellpLKfbHkD06E04P6d9ZQ24mat6hu4NSqun7WG0UhspHR5Myiv/510qouCjoo4+YJtNOqg5xHaFnCA==", + "dev": true, + "dependencies": { + "@csstools/postcss-cascade-layers": "^1.0.5", + "@csstools/postcss-color-function": "^1.1.1", + "@csstools/postcss-font-format-keywords": "^1.0.1", + "@csstools/postcss-hwb-function": "^1.0.2", + "@csstools/postcss-ic-unit": "^1.0.1", + "@csstools/postcss-is-pseudo-class": "^2.0.7", + "@csstools/postcss-nested-calc": "^1.0.0", + "@csstools/postcss-normalize-display-values": "^1.0.1", + "@csstools/postcss-oklab-function": "^1.1.1", + "@csstools/postcss-progressive-custom-properties": "^1.3.0", + "@csstools/postcss-stepped-value-functions": "^1.0.1", + "@csstools/postcss-text-decoration-shorthand": "^1.0.0", + "@csstools/postcss-trigonometric-functions": "^1.0.2", + "@csstools/postcss-unset-value": "^1.0.2", + "autoprefixer": "^10.4.8", + "browserslist": "^4.21.3", + "css-blank-pseudo": "^3.0.3", + "css-has-pseudo": "^3.0.4", + "css-prefers-color-scheme": "^6.0.3", + "cssdb": "^7.0.0", + "postcss-attribute-case-insensitive": "^5.0.2", + "postcss-clamp": "^4.1.0", + "postcss-color-functional-notation": "^4.2.4", + "postcss-color-hex-alpha": "^8.0.4", + "postcss-color-rebeccapurple": "^7.1.1", + "postcss-custom-media": "^8.0.2", + "postcss-custom-properties": "^12.1.8", + "postcss-custom-selectors": "^6.0.3", + "postcss-dir-pseudo-class": "^6.0.5", + "postcss-double-position-gradients": "^3.1.2", + "postcss-env-function": "^4.0.6", + "postcss-focus-visible": "^6.0.4", + "postcss-focus-within": "^5.0.4", + "postcss-font-variant": "^5.0.0", + "postcss-gap-properties": "^3.0.5", + "postcss-image-set-function": "^4.0.7", + "postcss-initial": "^4.0.1", + "postcss-lab-function": "^4.2.1", + "postcss-logical": "^5.0.4", + "postcss-media-minmax": "^5.0.0", + "postcss-nesting": "^10.1.10", + "postcss-opacity-percentage": "^1.1.2", + "postcss-overflow-shorthand": "^3.0.4", + "postcss-page-break": "^3.0.4", + "postcss-place": "^7.0.5", + "postcss-pseudo-class-any-link": "^7.1.6", + "postcss-replace-overflow-wrap": "^4.0.0", + "postcss-selector-not": "^6.0.1", + "postcss-value-parser": "^4.2.0" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-pseudo-class-any-link": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-7.1.6.tgz", + "integrity": "sha512-9sCtZkO6f/5ML9WcTLcIyV1yz9D1rf0tWc+ulKcvV30s0iZKS/ONyETvoWsr6vnrmW+X+KmuK3gV/w5EWnT37w==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "node_modules/postcss-replace-overflow-wrap": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", + "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", + "dev": true, + "peerDependencies": { + "postcss": "^8.0.3" + } + }, + "node_modules/postcss-selector-not": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-6.0.1.tgz", + "integrity": "sha512-1i9affjAe9xu/y9uqWH+tD4r6/hDaXJruk8xn2x1vzxC2U3J3LKO3zJW4CyxlNhA56pADJ/djpEwpH1RClI2rQ==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^12 || ^14 || >=16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + }, + "peerDependencies": { + "postcss": "^8.2" + } + }, + "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-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/preferred-pm": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/preferred-pm/-/preferred-pm-3.0.3.tgz", + "integrity": "sha512-+wZgbxNES/KlJs9q40F/1sfOd/j7f1O9JaHcW5Dsn3aUUOZg3L2bjpVUcKV2jvtElYfoTuQiNeMfQJ4kwUAhCQ==", + "dev": true, + "dependencies": { + "find-up": "^5.0.0", + "find-yarn-workspace-root2": "1.2.16", + "path-exists": "^4.0.0", + "which-pm": "2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/preferred-pm/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/preferred-pm/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/preferred-pm/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-plugin-java": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/prettier-plugin-java/-/prettier-plugin-java-2.1.0.tgz", + "integrity": "sha512-fALvx3PoabX6gar5Ock/reSEJVTmmJXowkvEXWhn6AxXnLfXoZxKqPNsUdQ+jcVaXO7jnQTW39hVGBnbfgFj+A==", + "dev": true, + "dependencies": { + "java-parser": "2.0.4", + "lodash": "4.17.21", + "prettier": "2.8.4" + } + }, + "node_modules/prettier-plugin-java/node_modules/prettier": { + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz", + "integrity": "sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-plugin-packagejson": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/prettier-plugin-packagejson/-/prettier-plugin-packagejson-2.4.3.tgz", + "integrity": "sha512-kPeeviJiwy0BgOSk7No8NmzzXfW4R9FYWni6ziA5zc1kGVVrKnBzMZdu2TUhI+I7h8/5Htt3vARYOk7KKJTTNQ==", + "dev": true, + "dependencies": { + "sort-package-json": "2.4.1", + "synckit": "0.8.5" + }, + "peerDependencies": { + "prettier": ">= 1.16.0" + }, + "peerDependenciesMeta": { + "prettier": { + "optional": true + } + } + }, + "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": "28.1.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", + "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dev": true, + "dependencies": { + "@jest/schemas": "^28.1.3", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.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/proc-log": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-2.0.1.tgz", + "integrity": "sha512-Kcmo2FhfDTXdcbfDH76N7uBYHINxc/8GW7UAVuVP9I+Va3uHSerrnKV6dLooga/gh7GlgzuCCr/eoldnL1muGw==", + "dev": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.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==", + "dev": true + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/promise": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-8.2.0.tgz", + "integrity": "sha512-+CMAlLHqwRYwBMXKCP+o8ns7DN+xHDUiI+0nArsiJ9y+kJVPLFxEaSw6Ha9s9H0tftxg2Yzl25wqj9G7m5wLZg==", + "dev": true, + "dependencies": { + "asap": "~2.0.6" + } + }, + "node_modules/promise-all-reject-late": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-all-reject-late/-/promise-all-reject-late-1.0.1.tgz", + "integrity": "sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/promise-call-limit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-call-limit/-/promise-call-limit-1.0.1.tgz", + "integrity": "sha512-3+hgaa19jzCGLuSCbieeRsu5C2joKfYn8pY6JAuXFRVfF4IO+L7UPpFWNTeWT9pM7uhskvbPPd/oEOktCn317Q==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "dev": true + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "dev": true, + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "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/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/proxy-addr/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/proxy-from-env": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", + "integrity": "sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A==", + "dev": true + }, + "node_modules/prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", + "dev": true, + "optional": true + }, + "node_modules/psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", + "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==", + "dev": true, + "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==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.11.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.1.tgz", + "integrity": "sha512-0wsrzgTz/kAVIeuxSjnpGC56rzYtr6JT/2BwEvMaPhFIoYa1aGO8LbzuU1R0uUYQkLpWBTOj0l/CLAJB64J6nQ==", + "dev": true, + "dependencies": { + "side-channel": "^1.0.4" + }, + "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": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", + "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==", + "dev": true + }, + "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/randexp": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.5.3.tgz", + "integrity": "sha512-U+5l2KrcMNOUPYvazA3h5ekF80FHTUG+87SEAmHZmolh1M+i/WyTCxVzmi+tidIa1tM4BSe8g2Y/D3loWDjj+w==", + "dev": true, + "dependencies": { + "drange": "^1.0.2", + "ret": "^0.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "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.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dev": true, + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/read-cmd-shim": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-3.0.1.tgz", + "integrity": "sha512-kEmDUoYf/CDy8yZbLTmhB1X9kkjf9Q80PCNsDMb7ufrGd6zZSQA1+UyjrO+pZm5K/S4OXCWJeiIt1JA8kAsa6g==", + "dev": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/read-package-json": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-5.0.2.tgz", + "integrity": "sha512-BSzugrt4kQ/Z0krro8zhTwV1Kd79ue25IhNN/VtHFy1mG/6Tluyi+msc0UpwaoQzxSHa28mntAjIZY6kEgfR9Q==", + "dev": true, + "dependencies": { + "glob": "^8.0.1", + "json-parse-even-better-errors": "^2.3.1", + "normalize-package-data": "^4.0.0", + "npm-normalize-package-bin": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/read-package-json-fast": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-2.0.3.tgz", + "integrity": "sha512-W/BKtbL+dUjTuRL2vziuYhp76s5HZ9qQhd/dKfWIZveD0O40453QNyZhC0e63lqZrAQ4jiOapVoeJ7JrszenQQ==", + "dev": true, + "dependencies": { + "json-parse-even-better-errors": "^2.3.0", + "npm-normalize-package-bin": "^1.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/read-package-json/node_modules/npm-normalize-package-bin": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-2.0.0.tgz", + "integrity": "sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ==", + "dev": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "node_modules/read-pkg/node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/read-pkg/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "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/readdir-scoped-modules": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz", + "integrity": "sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==", + "dev": true, + "dependencies": { + "debuglog": "^1.0.1", + "dezalgo": "^1.0.0", + "graceful-fs": "^4.1.2", + "once": "^1.3.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", + "dev": true, + "dependencies": { + "resolve": "^1.1.6" + }, + "engines": { + "node": ">= 0.10" + } + }, + "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==" + }, + "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.15.0", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.0.tgz", + "integrity": "sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg==", + "dev": true, + "dependencies": { + "@babel/runtime": "^7.8.4" + } + }, + "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-to-ast": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/regexp-to-ast/-/regexp-to-ast-0.5.0.tgz", + "integrity": "sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw==", + "dev": true + }, + "node_modules/regexp.prototype.flags": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexpu-core": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.1.0.tgz", + "integrity": "sha512-bb6hk+xWd2PEOkj5It46A16zFMs2mv86Iwpdu94la4S3sJ7C973h2dHpYKwIBGaWSO7cIRJ+UX0IeMaWcO4qwA==", + "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/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": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "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": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", + "dev": true + }, + "node_modules/replace-ext": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz", + "integrity": "sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", + "dev": true, + "dependencies": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/request-progress": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz", + "integrity": "sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg==", + "dev": true, + "dependencies": { + "throttleit": "^1.0.0" + } + }, + "node_modules/request/node_modules/http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", + "dev": true, + "dependencies": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" + } + }, + "node_modules/request/node_modules/jsprim": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "dev": true, + "dependencies": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/request/node_modules/qs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/request/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/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "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==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true + }, + "node_modules/resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.9.0", + "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-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-url-loader": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-5.0.0.tgz", + "integrity": "sha512-uZtduh8/8srhBoMx//5bwqjQ+rfYOUq8zC9NrMUGtjBiGTtFJM42s58/36+hTqeqINcnYe08Nj3LkK9lW4N8Xg==", + "dev": true, + "dependencies": { + "adjust-sourcemap-loader": "^4.0.0", + "convert-source-map": "^1.7.0", + "loader-utils": "^2.0.0", + "postcss": "^8.2.14", + "source-map": "0.6.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/resolve-url-loader/node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, + "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/resp-modifier": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/resp-modifier/-/resp-modifier-6.0.2.tgz", + "integrity": "sha512-U1+0kWC/+4ncRFYqQWTx/3qkfE6a4B/h3XXgmXypfa0SPZ3t7cbbaFk297PjQS/yov24R18h6OZe6iZwj3NSLw==", + "dev": true, + "dependencies": { + "debug": "^2.2.0", + "minimatch": "^3.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/resp-modifier/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/resp-modifier/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/resp-modifier/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/resp-modifier/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "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==", + "dev": true, + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ret": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.2.2.tgz", + "integrity": "sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "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/rimraf/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/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/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/run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true, + "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/rx": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/rx/-/rx-4.1.0.tgz", + "integrity": "sha512-CiaiuN6gapkdl+cZUr67W6I8jquN4lkak3vtIsIWCl4XIPP8ffsoyN6/+PuGXnQy8Cu8W2y9Xxh31Rq4M6wUug==", + "dev": true + }, + "node_modules/rxjs": { + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.6.tgz", + "integrity": "sha512-dnyv2/YsXhnm461G+R/Pe5bWP41Nm6LBXEYWI6eiFP4fiwx6WRI/CD0zbdVAudd9xwLEF2IDcKXLHit0FYjUzw==", + "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-stable-stringify": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.3.1.tgz", + "integrity": "sha512-kYBSfT+troD9cDA85VDnHZ1rpHC50O0g1e6WlGHVCz/g+JS+9WKLj+XwFYyR8UbrZN8ll9HUpDAAddY58MGisg==", + "dev": true, + "engines": { + "node": ">=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==", + "dev": true + }, + "node_modules/sass": { + "version": "1.54.4", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.54.4.tgz", + "integrity": "sha512-3tmF16yvnBwtlPrNBHw/H907j8MlOX8aTBnlNX1yrKx24RKcJGPyLhFUwkoKBKesR3unP93/2z14Ll8NicwQUA==", + "dev": true, + "dependencies": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/sass-loader": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.0.2.tgz", + "integrity": "sha512-BbiqbVmbfJaWVeOOAu2o7DhYWtcNmTfvroVgFXa6k2hHheMxNAeDHLNoDy/Q5aoaVlz0LH+MbMktKwm9vN/j8Q==", + "dev": true, + "dependencies": { + "klona": "^2.0.4", + "neo-async": "^2.6.2" + }, + "engines": { + "node": ">= 14.15.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 || ^7.0.0", + "sass": "^1.3.0", + "sass-embedded": "*", + "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "fibers": { + "optional": true + }, + "node-sass": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + } + } + }, + "node_modules/sass/node_modules/immutable": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.1.0.tgz", + "integrity": "sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==", + "dev": true + }, + "node_modules/sax": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", + "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==", + "dev": true + }, + "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/scoped-regex": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/scoped-regex/-/scoped-regex-2.1.0.tgz", + "integrity": "sha512-g3WxHrqSWCZHGHlSrF51VXFdjImhwvH8ZO/pryFH56Qi0cDsZfylQa/t0jCzVQFNbNvM00HfHjkDPEuarKDSWQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", + "dev": true + }, + "node_modules/selfsigned": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.1.1.tgz", + "integrity": "sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ==", + "dev": true, + "dependencies": { + "node-forge": "^1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/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/send": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", + "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", + "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.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "~2.3.0", + "range-parser": "~1.2.0", + "statuses": "~1.4.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/http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "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/send/node_modules/inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "dev": true + }, + "node_modules/send/node_modules/mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", + "dev": true, + "bin": { + "mime": "cli.js" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "node_modules/send/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/send/node_modules/statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", + "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", + "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": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", + "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": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "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": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "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": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "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-index/node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/serve-static": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", + "dev": true, + "dependencies": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.2", + "send": "0.16.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/server-destroy": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/server-destroy/-/server-destroy-1.0.1.tgz", + "integrity": "sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ==", + "dev": true + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true + }, + "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/shell-quote": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", + "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/shelljs": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", + "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", + "dev": true, + "dependencies": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + }, + "bin": { + "shjs": "bin/shjs" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/shelljs/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/shelljs/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/shelljs/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/shellwords": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", + "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", + "dev": true + }, + "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==", + "dev": true + }, + "node_modules/simple-git": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.13.0.tgz", + "integrity": "sha512-VYrs3joeHvWGcN3K135RpGpPjm4AHYeOrclwew6LlfHgq6ozQYIW2yMnmjf4PCgVOuSYCbXkdUjyiFawuJz8MA==", + "dev": true, + "dependencies": { + "@kwsites/file-exists": "^1.1.1", + "@kwsites/promise-deferred": "^1.1.1", + "debug": "^4.3.4" + }, + "funding": { + "type": "github", + "url": "https://github.com/steveukx/git-js?sponsor=1" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dev": true, + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "dev": true + }, + "node_modules/sirv": { + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-1.0.19.tgz", + "integrity": "sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ==", + "dev": true, + "dependencies": { + "@polka/url": "^1.0.0-next.20", + "mrmime": "^1.0.0", + "totalist": "^1.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "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.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.1.1.tgz", + "integrity": "sha512-qDOv24WjnYuL+wbwHdlsYZFy+cgPtrYw0Tn7GLORicQp9BkQLzrgI3Pm4VyR9ERZ41YTn7KlMPuL1n05WdZvmg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true, + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socket.io": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.6.1.tgz", + "integrity": "sha512-KMcaAi4l/8+xEjkRICl6ak8ySoxsYG+gG6/XfRCPJPQ/haCRIJBTL4wIl8YCsmtaBovcAXGLOShyVWQ/FG8GZA==", + "dev": true, + "dependencies": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "debug": "~4.3.2", + "engine.io": "~6.4.1", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-adapter": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz", + "integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==", + "dev": true, + "dependencies": { + "ws": "~8.11.0" + } + }, + "node_modules/socket.io-client": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.6.1.tgz", + "integrity": "sha512-5UswCV6hpaRsNg5kkEHVcbBIXEYoVbMQaHJBXJCyEQ+CiFPV1NIOY0XOFWG4XR4GZcB8Kn6AsRs/9cy9TbqVMQ==", + "dev": true, + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.4.0", + "socket.io-parser": "~4.2.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.3.tgz", + "integrity": "sha512-JMafRntWVO2DCJimKsRTh/wnqVvO4hrfwOqtO7f+uzwsQMuxO6VwImtYxaQ+ieoyshWOTJyV0fA21lccEXRPpQ==", + "dev": true, + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.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/socks": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.0.tgz", + "integrity": "sha512-scnOe9y4VuiNUULJN72GrM26BNOjVsfPXI+j+98PkyEfsIXroa5ofyjT+FzGvn/xHs73U2JtoBYAVx9Hl4quSA==", + "dev": true, + "dependencies": { + "ip": "^2.0.0", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.13.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", + "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", + "dev": true, + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/sort-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-4.2.0.tgz", + "integrity": "sha512-aUYIEU/UviqPgc8mHR6IW1EGxkAXpeRETYcrzg8cLAvUPZcpAlleSXHV2mY7G12GphSH6Gzv+4MMVSSkbdteHg==", + "dev": true, + "dependencies": { + "is-plain-obj": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/sort-object-keys": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sort-object-keys/-/sort-object-keys-1.1.3.tgz", + "integrity": "sha512-855pvK+VkU7PaKYPc+Jjnmt4EzejQHyhhF33q31qG8x7maDzkeFhAAThdCYay11CISO+qAMwjOBP+fPZe0IPyg==", + "dev": true + }, + "node_modules/sort-package-json": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/sort-package-json/-/sort-package-json-2.4.1.tgz", + "integrity": "sha512-Nd3rgLBJcZ4iw7tpuOhwBupG6SvUDU0Fy1cZGAMorA2JmDUb+29Dg5phJK9gapa2Ak9d15w/RuMl/viwX+nKwQ==", + "dev": true, + "dependencies": { + "detect-indent": "^7.0.1", + "detect-newline": "^4.0.0", + "git-hooks-list": "^3.0.0", + "globby": "^13.1.2", + "is-plain-obj": "^4.1.0", + "sort-object-keys": "^1.1.3" + }, + "bin": { + "sort-package-json": "cli.js" + } + }, + "node_modules/sort-package-json/node_modules/detect-newline": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-4.0.0.tgz", + "integrity": "sha512-1aXUEPdfGdzVPFpzGJJNgq9o81bGg1s09uxTWsqBlo9PI332uyJRQq13+LK/UN4JfxJbFdCXonUFQ9R/p7yCtw==", + "dev": true, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/sort-package-json/node_modules/globby": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.4.tgz", + "integrity": "sha512-iui/IiiW+QrJ1X1hKH5qwlMQyv34wJAYwH1vrf8b9kBA4sNiif3gKsMHa+BrdnOpEudWjpotfa7LrTzB1ERS/g==", + "dev": true, + "dependencies": { + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.11", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/sort-package-json/node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/sort-package-json/node_modules/slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "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/source-map-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-4.0.0.tgz", + "integrity": "sha512-i3KVgM3+QPAHNbGavK+VBq03YoJl24m9JWNbLgsjTj8aJzXG9M61bantBTNBt7CNwY2FYf+RJRYJ3pzalKjIrw==", + "dev": true, + "dependencies": { + "abab": "^2.0.6", + "iconv-lite": "^0.6.3", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.72.1" + } + }, + "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-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "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/sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==" + }, + "node_modules/spawn-command": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2.tgz", + "integrity": "sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==", + "dev": true + }, + "node_modules/spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", + "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==", + "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/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "node_modules/sshpk": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", + "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", + "dev": true, + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ssri": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz", + "integrity": "sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==", + "dev": true, + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "dev": true, + "engines": { + "node": "*" + } + }, + "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/statuses": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", + "integrity": "sha512-wuTCPGlJONk/a1kqZ4fQM2+908lC7fa7nPYpTC1EhnvqLX/IICbeP1OZGDtA374trpSq68YubKUMo8oRhN46yg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/stream-throttle": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/stream-throttle/-/stream-throttle-0.1.3.tgz", + "integrity": "sha512-889+B9vN9dq7/vLbGyuHeZ6/ctf5sNuGWsDy89uNxkFTAgzy0eK7+w5fL3KLNRTkLle7EgZGvHUphZW0Q26MnQ==", + "dev": true, + "dependencies": { + "commander": "^2.2.0", + "limiter": "^1.0.5" + }, + "bin": { + "throttleproxy": "bin/throttleproxy.js" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/stream-throttle/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/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.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", + "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", + "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + }, + "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-bom-buf": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-buf/-/strip-bom-buf-1.0.0.tgz", + "integrity": "sha512-1sUIL1jck0T1mhOLP2c696BIznzT525Lkub+n4jjMHjhjhoAQA6Ye659DxdlZBr0aLDMQoTxKIpnlqxgtwjsuQ==", + "dev": true, + "dependencies": { + "is-utf8": "^0.2.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-bom-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-2.0.0.tgz", + "integrity": "sha512-yH0+mD8oahBZWnY43vxs4pSinn8SMKAdml/EOGBewoe1Y0Eitd0h2Mg3ZRiXruUW6L4P+lvZiEgbh0NgUGia1w==", + "dev": true, + "dependencies": { + "first-chunk-stream": "^2.0.0", + "strip-bom": "^2.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-bom-stream/node_modules/strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g==", + "dev": true, + "dependencies": { + "is-utf8": "^0.2.0" + }, + "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/stylus": { + "version": "0.59.0", + "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.59.0.tgz", + "integrity": "sha512-lQ9w/XIOH5ZHVNuNbWW8D822r+/wBSO/d6XvtyHLF7LW4KaCIDeVbvn5DF8fGCJAUCwVhVi/h6J0NUcnylUEjg==", + "dev": true, + "dependencies": { + "@adobe/css-tools": "^4.0.1", + "debug": "^4.3.2", + "glob": "^7.1.6", + "sax": "~1.2.4", + "source-map": "^0.7.3" + }, + "bin": { + "stylus": "bin/stylus" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://opencollective.com/stylus" + } + }, + "node_modules/stylus-loader": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-7.0.0.tgz", + "integrity": "sha512-WTbtLrNfOfLgzTaR9Lj/BPhQroKk/LC1hfTXSUbrxmxgfUo3Y3LpmKRVA2R1XbjvTAvOfaian9vOyfv1z99E+A==", + "dev": true, + "dependencies": { + "fast-glob": "^3.2.11", + "klona": "^2.0.5", + "normalize-path": "^3.0.0" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "stylus": ">=0.52.4", + "webpack": "^5.0.0" + } + }, + "node_modules/stylus/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/stylus/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/stylus/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/stylus/node_modules/sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + }, + "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==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/supports-hyperlinks": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", + "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.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/swagger-ui-dist": { + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-4.19.0.tgz", + "integrity": "sha512-9C9fJGI18gK5AhaU5YRyPY1lXJH4lmWh8h9zFMrJBkYzdRjCbAzYl1ayWPYgwFvag/Luqi3Co599OK/39IS2QQ==", + "dev": true + }, + "node_modules/symbol-observable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", + "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "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/synckit": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz", + "integrity": "sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==", + "dev": true, + "dependencies": { + "@pkgr/utils": "^2.3.1", + "tslib": "^2.5.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "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/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.14.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz", + "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==", + "dev": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser-webpack-plugin": { + "version": "5.3.8", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.8.tgz", + "integrity": "sha512-WiHL3ElchZMsK27P8uIUh4604IgJyAW47LVXGbEoB21DbQcZ+OuMpGjVYnEUaqcWM6dO8uS2qUbA7LSCWqvsbg==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.17", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.1", + "terser": "^5.16.8" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "uglify-js": { + "optional": true + } + } + }, + "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/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/terser-webpack-plugin/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/terser-webpack-plugin/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/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/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/terser-webpack-plugin/node_modules/terser": { + "version": "5.17.4", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.17.4.tgz", + "integrity": "sha512-jcEKZw6UPrgugz/0Tuk/PVyLAPfMBJf5clnGueo45wTweoV8yh7Q7PEkhkJ5uuUbC7zAxEcG3tqNr1bstkQ8nw==", + "dev": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "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/test-exclude/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/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/test-exclude/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/text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", + "dev": true + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/textextensions": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-5.15.0.tgz", + "integrity": "sha512-MeqZRHLuaGamUXGuVn2ivtU3LA3mLCCIO5kUGoohTCoGmCBg/+8yPhWVX9WSl9telvVd8erftjFk9Fwb2dD6rw==", + "dev": true, + "engines": { + "node": ">=0.8" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, + "node_modules/then-request": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/then-request/-/then-request-6.0.2.tgz", + "integrity": "sha512-3ZBiG7JvP3wbDzA9iNY5zJQcHL4jn/0BWtXIkagfz7QgOL/LqjCEOBQuJNZfu0XYnv5JhKh+cDxCPM4ILrqruA==", + "dev": true, + "dependencies": { + "@types/concat-stream": "^1.6.0", + "@types/form-data": "0.0.33", + "@types/node": "^8.0.0", + "@types/qs": "^6.2.31", + "caseless": "~0.12.0", + "concat-stream": "^1.6.0", + "form-data": "^2.2.0", + "http-basic": "^8.1.1", + "http-response-object": "^3.0.1", + "promise": "^8.0.0", + "qs": "^6.4.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/then-request/node_modules/@types/node": { + "version": "8.10.66", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.66.tgz", + "integrity": "sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==", + "dev": true + }, + "node_modules/throttleit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz", + "integrity": "sha512-rkTVqu6IjfQ/6+uNuuc3sZek4CEYxTJom3IktzgdSxcZqdARuebbA/f4QmAxMQIxqq9ZLEUkSYqvuk1I6VKq4g==", + "dev": true + }, + "node_modules/through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true + }, + "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/tiny-glob": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.9.tgz", + "integrity": "sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==", + "dev": true, + "dependencies": { + "globalyzer": "0.1.0", + "globrex": "^0.1.2" + } + }, + "node_modules/tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "dependencies": { + "rimraf": "^3.0.0" + }, + "engines": { + "node": ">=8.17.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": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", + "engines": { + "node": ">=4" + } + }, + "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==", + "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/totalist": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-1.1.0.tgz", + "integrity": "sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "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==", + "dev": true, + "dependencies": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "dev": true, + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "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/treeverse": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/treeverse/-/treeverse-1.0.4.tgz", + "integrity": "sha512-whw60l7r+8ZU8Tu/Uc2yxtc4ZTZbR/PF3u1IPNKGQ6p8EICLb3Z2lAgoqw9bqYd8IkgnsaOcLzYHFckjqNsf0g==", + "dev": true + }, + "node_modules/triple-beam": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", + "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==", + "dev": true + }, + "node_modules/ts-jest": { + "version": "28.0.8", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-28.0.8.tgz", + "integrity": "sha512-5FaG0lXmRPzApix8oFG8RKjAz4ehtm8yMKOTy5HX3fY6W8kmvOrmcY0hKDElW52FJov+clhUbrKAqofnj4mXTg==", + "dev": true, + "dependencies": { + "bs-logger": "0.x", + "fast-json-stable-stringify": "2.x", + "jest-util": "^28.0.0", + "json5": "^2.2.1", + "lodash.memoize": "4.x", + "make-error": "1.x", + "semver": "7.x", + "yargs-parser": "^21.0.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + }, + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/types": "^28.0.0", + "babel-jest": "^28.0.0", + "jest": "^28.0.0", + "typescript": ">=4.3" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "@jest/types": { + "optional": true + }, + "babel-jest": { + "optional": true + }, + "esbuild": { + "optional": true + } + } + }, + "node_modules/ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.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.1", + "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.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "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": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/tslib": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.2.tgz", + "integrity": "sha512-5svOrSA2w3iGFDs1HibEVBGbDrAY82bFQ3HZ3ixB+88nsbsWQoKqDRb5UBYAUPEzbBn6dAp5gRNXglySbx1MlA==" + }, + "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/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", + "dev": true + }, + "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==", + "dev": true, + "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/typed-assert": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/typed-assert/-/typed-assert-1.0.9.tgz", + "integrity": "sha512-KNNZtayBCtmnNmbo5mG47p1XsCyrx6iVqomjcZnec/1Y5GGARaxPs6r49RnSPeUP3YjNYiU9sQHAtY4BBvnZwg==", + "dev": true + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "dev": true + }, + "node_modules/typescript": { + "version": "4.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.2.tgz", + "integrity": "sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/ua-parser-js": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.34.tgz", + "integrity": "sha512-K9mwJm/DaB6mRLZfw6q8IMXipcrmuT6yfhYmwhAkuh+81sChuYstYA+znlgaflUPaYUa3odxKPKGw6Vw/lANew==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/ua-parser-js" + }, + { + "type": "paypal", + "url": "https://paypal.me/faisalman" + } + ], + "engines": { + "node": "*" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/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/universal-user-agent": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", + "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==", + "dev": true + }, + "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==", + "dev": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.8.tgz", + "integrity": "sha512-GHg7C4M7oJSJYW/ED/5QOJ7nL/E0lwTOBGsOorA7jqHr8ExUhPfwAotIAmdSw/LWv3SMLSNpzTAgeLG9zaZKTA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "browserslist-lint": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.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==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/url": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", + "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==", + "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==", + "dev": true, + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/url/node_modules/punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==", + "dev": true + }, + "node_modules/util": { + "version": "0.12.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz", + "integrity": "sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==", + "dev": true, + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "safe-buffer": "^5.1.2", + "which-typed-array": "^1.1.2" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "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-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "node_modules/v8-to-istanbul": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz", + "integrity": "sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/validate-npm-package-name": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-4.0.0.tgz", + "integrity": "sha512-mzR0L8ZDktZjpX4OB46KT+56MAhl4EIazWP/+G/HPGuvfdaqg4YsCdtOm6U9+LOFyYDoh4dpnpxZRB9MQQns5Q==", + "dev": true, + "dependencies": { + "builtins": "^5.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "node_modules/vinyl": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.1.tgz", + "integrity": "sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw==", + "dev": true, + "dependencies": { + "clone": "^2.1.1", + "clone-buffer": "^1.0.0", + "clone-stats": "^1.0.0", + "cloneable-readable": "^1.0.0", + "remove-trailing-separator": "^1.0.1", + "replace-ext": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/vinyl-file": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/vinyl-file/-/vinyl-file-3.0.0.tgz", + "integrity": "sha512-BoJDj+ca3D9xOuPEM6RWVtWQtvEPQiQYn82LvdxhLWplfQsBzBqtgK0yhCP0s1BNTi6dH9BO+dzybvyQIacifg==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "pify": "^2.3.0", + "strip-bom-buf": "^1.0.0", + "strip-bom-stream": "^2.0.0", + "vinyl": "^2.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/vinyl/node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "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": "3.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-3.0.0.tgz", + "integrity": "sha512-3WFqGEgSXIyGhOmAFtlicJNMjEps8b1MG31NCA0/vOF9+nKMUW1ckhi9cnNHmf88Rzw5V+dwIwsm2C7X8k9aQg==", + "dev": true, + "dependencies": { + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/wait-on": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-7.0.1.tgz", + "integrity": "sha512-9AnJE9qTjRQOlTZIldAaf/da2eW0eSRSgcqq85mXQja/DW3MriHxkpODDSUEg+Gri/rKEcXUZHe+cevvYItaog==", + "dev": true, + "dependencies": { + "axios": "^0.27.2", + "joi": "^17.7.0", + "lodash": "^4.17.21", + "minimist": "^1.2.7", + "rxjs": "^7.8.0" + }, + "bin": { + "wait-on": "bin/wait-on" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/wait-on/node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/walk-up-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/walk-up-path/-/walk-up-path-1.0.0.tgz", + "integrity": "sha512-hwj/qMDUEjCU5h0xr90KGCf0tg0/LgJbmOWgrWKYlcJZM7XvquvUJZ0G/HMGr7F7OQMOUuPHWP9JpriinkAlkg==", + "dev": true + }, + "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.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "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": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/webpack": { + "version": "5.74.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.74.0.tgz", + "integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==", + "dev": 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.7.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.10.0", + "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-even-better-errors": "^2.3.1", + "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.4.0", + "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-bundle-analyzer": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.6.1.tgz", + "integrity": "sha512-oKz9Oz9j3rUciLNfpGFjOb49/jEpXNmWdVH8Ls//zNcnLlQdTGXQQMsBbb/gR7Zl8WNLxVCq+0Hqbx3zv6twBw==", + "dev": true, + "dependencies": { + "acorn": "^8.0.4", + "acorn-walk": "^8.0.0", + "chalk": "^4.1.0", + "commander": "^7.2.0", + "gzip-size": "^6.0.0", + "lodash": "^4.17.20", + "opener": "^1.5.2", + "sirv": "^1.0.7", + "ws": "^7.3.1" + }, + "bin": { + "webpack-bundle-analyzer": "lib/bin/analyzer.js" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/webpack-bundle-analyzer/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/webpack-bundle-analyzer/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/webpack-bundle-analyzer/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "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/webpack-bundle-analyzer/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/webpack-bundle-analyzer/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==", + "dev": true + }, + "node_modules/webpack-bundle-analyzer/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==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/webpack-bundle-analyzer/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/webpack-bundle-analyzer/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/webpack-bundle-analyzer/node_modules/ws": { + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "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/webpack-dev-middleware": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz", + "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==", + "dev": true, + "dependencies": { + "colorette": "^2.0.10", + "memfs": "^3.4.3", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^4.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/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack-dev-server": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.10.0.tgz", + "integrity": "sha512-7dezwAs+k6yXVFZ+MaL8VnE+APobiO3zvpp3rBHe/HmWQ+avwh0Q3d0xxacOiBybZZ3syTZw9HXzpa3YNbAZDQ==", + "dev": true, + "dependencies": { + "@types/bonjour": "^3.5.9", + "@types/connect-history-api-fallback": "^1.3.5", + "@types/express": "^4.17.13", + "@types/serve-index": "^1.9.1", + "@types/serve-static": "^1.13.10", + "@types/sockjs": "^0.3.33", + "@types/ws": "^8.5.1", + "ansi-html-community": "^0.0.8", + "bonjour-service": "^1.0.11", + "chokidar": "^3.5.3", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^2.0.0", + "default-gateway": "^6.0.3", + "express": "^4.17.3", + "graceful-fs": "^4.2.6", + "html-entities": "^2.3.2", + "http-proxy-middleware": "^2.0.3", + "ipaddr.js": "^2.0.1", + "open": "^8.0.9", + "p-retry": "^4.5.0", + "rimraf": "^3.0.2", + "schema-utils": "^4.0.0", + "selfsigned": "^2.0.1", + "serve-index": "^1.9.1", + "sockjs": "^0.3.24", + "spdy": "^4.0.2", + "webpack-dev-middleware": "^5.3.1", + "ws": "^8.4.2" + }, + "bin": { + "webpack-dev-server": "bin/webpack-dev-server.js" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.37.0 || ^5.0.0" + }, + "peerDependenciesMeta": { + "webpack-cli": { + "optional": true + } + } + }, + "node_modules/webpack-dev-server/node_modules/connect-history-api-fallback": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", + "dev": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/webpack-dev-server/node_modules/schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/webpack-merge": { + "version": "5.9.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.9.0.tgz", + "integrity": "sha512-6NbRQw4+Sy50vYNTw7EyOn41OZItPiXB8GNv3INSoe3PSFaHJEz3SHTrYVaRm2LilNGnFUzh0FAwqPEmU/CwDg==", + "dev": true, + "dependencies": { + "clone-deep": "^4.0.1", + "wildcard": "^2.0.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/webpack-notifier": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/webpack-notifier/-/webpack-notifier-1.15.0.tgz", + "integrity": "sha512-N2V8UMgRB5komdXQRavBsRpw0hPhJq2/SWNOGuhrXpIgRhcMexzkGQysUyGStHLV5hkUlgpRiF7IUXoBqyMmzQ==", + "dev": true, + "dependencies": { + "node-notifier": "^9.0.0", + "strip-ansi": "^6.0.0" + }, + "peerDependencies": { + "@types/webpack": ">4.41.31" + }, + "peerDependenciesMeta": { + "@types/webpack": { + "optional": true + } + } + }, + "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/webpack-subresource-integrity": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-5.1.0.tgz", + "integrity": "sha512-sacXoX+xd8r4WKsy9MvH/q/vBtEHr86cpImXwyg74pFIpERKt6FmB8cXpeuh0ZLgclOlHI4Wcll7+R5L02xk9Q==", + "dev": true, + "dependencies": { + "typed-assert": "^1.0.8" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "html-webpack-plugin": ">= 5.0.0-beta.1 < 6", + "webpack": "^5.12.0" + }, + "peerDependenciesMeta": { + "html-webpack-plugin": { + "optional": true + } + } + }, + "node_modules/webpack/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/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, + "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, + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/webpack/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/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 + }, + "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, + "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/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": "2.0.0", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", + "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "dev": true, + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-encoding/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/whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-10.0.0.tgz", + "integrity": "sha512-CLxxCmdUby142H5FZzn4D8ikO1cmypvXVQktsgosNy4a4BHrDHeciBBGZhb0bNoR5/MltoCatso+vFjjGx8t0w==", + "dev": true, + "dependencies": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "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-pm": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-pm/-/which-pm-2.0.0.tgz", + "integrity": "sha512-Lhs9Pmyph0p5n5Z3mVnN0yWcbQYUAD7rbQUiMsQxOJ3T57k7RFe35SUwWMf7dsbDZks1uOmw4AecB/JMDj3v/w==", + "dev": true, + "dependencies": { + "load-yaml-file": "^0.2.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8.15" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.8.tgz", + "integrity": "sha512-Jn4e5PItbcAHyLoRDwvPj1ypu27DJbtdYXUa5zsinrUx77Uvfb0cXwwnGMTn7cjUfhhqgVQnVJCwF+7cgU7tpw==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-abstract": "^1.20.0", + "for-each": "^0.3.3", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dev": true, + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "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/windows-release": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-4.0.0.tgz", + "integrity": "sha512-OxmV4wzDKB1x7AZaZgXMVsdJ1qER1ed83ZrTYd5Bwq2HfJVg3DJS8nqlAG4sMoJ7mu8cuRmLEYyU13BKwctRAg==", + "dev": true, + "dependencies": { + "execa": "^4.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/winston": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.8.1.tgz", + "integrity": "sha512-r+6YAiCR4uI3N8eQNOg8k3P3PqwAm20cLKlzVD9E66Ch39+LZC+VH1UKf9JemQj2B3QoUHfKD7Poewn0Pr3Y1w==", + "dev": true, + "dependencies": { + "@dabh/diagnostics": "^2.0.2", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.4.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.5.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.5.0.tgz", + "integrity": "sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q==", + "dev": true, + "dependencies": { + "logform": "^2.3.2", + "readable-stream": "^3.6.0", + "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 6.4.0" + } + }, + "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==", + "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/wrap-ansi/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/wrap-ansi/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/wrap-ansi/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/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", + "integrity": "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==", + "dev": true + }, + "node_modules/xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/xml2js": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", + "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", + "dev": true, + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~9.0.1" + } + }, + "node_modules/xmlbuilder": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", + "integrity": "sha512-7YXTQc3P2l9+0rjaUbLwMKRhtmwg1M1eDf6nag7urC7pIPYLD9W/jmzQ4ptRSUbodw5S0jfoGTflLemQibSpeQ==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "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/xmlhttprequest-ssl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", + "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "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": "2.2.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz", + "integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, + "node_modules/yargs": { + "version": "17.5.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", + "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", + "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": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "engines": { + "node": ">=12" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/yeoman-environment": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yeoman-environment/-/yeoman-environment-3.10.0.tgz", + "integrity": "sha512-sYtSxBK9daq21QjoskJTHKLQ1xEsRPURkmFV/aM8HS8ZlQVzwx57Rz1zCs8EGPhK4vqsmTE8H92Gp1jg1fT3EA==", + "dev": true, + "dependencies": { + "@npmcli/arborist": "^4.0.4", + "are-we-there-yet": "^2.0.0", + "arrify": "^2.0.1", + "binaryextensions": "^4.15.0", + "chalk": "^4.1.0", + "cli-table": "^0.3.1", + "commander": "7.1.0", + "dateformat": "^4.5.0", + "debug": "^4.1.1", + "diff": "^5.0.0", + "error": "^10.4.0", + "escape-string-regexp": "^4.0.0", + "execa": "^5.0.0", + "find-up": "^5.0.0", + "globby": "^11.0.1", + "grouped-queue": "^2.0.0", + "inquirer": "^8.0.0", + "is-scoped": "^2.1.0", + "isbinaryfile": "^4.0.10", + "lodash": "^4.17.10", + "log-symbols": "^4.0.0", + "mem-fs": "^1.2.0 || ^2.0.0", + "mem-fs-editor": "^8.1.2 || ^9.0.0", + "minimatch": "^3.0.4", + "npmlog": "^5.0.1", + "p-queue": "^6.6.2", + "p-transform": "^1.3.0", + "pacote": "^12.0.2", + "preferred-pm": "^3.0.3", + "pretty-bytes": "^5.3.0", + "semver": "^7.1.3", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0", + "text-table": "^0.2.0", + "textextensions": "^5.12.0", + "untildify": "^4.0.0" + }, + "bin": { + "yoe": "cli/index.js" + }, + "engines": { + "node": ">=12.10.0" + }, + "peerDependencies": { + "mem-fs": "^1.2.0 || ^2.0.0", + "mem-fs-editor": "^8.1.2 || ^9.0.0" + } + }, + "node_modules/yeoman-environment/node_modules/@npmcli/fs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", + "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", + "dev": true, + "dependencies": { + "@gar/promisify": "^1.0.1", + "semver": "^7.3.5" + } + }, + "node_modules/yeoman-environment/node_modules/@npmcli/git": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-2.1.0.tgz", + "integrity": "sha512-/hBFX/QG1b+N7PZBFs0bi+evgRZcK9nWBxQKZkGoXUT5hJSwl5c4d7y8/hm+NQZRPhQ67RzFaj5UM9YeyKoryw==", + "dev": true, + "dependencies": { + "@npmcli/promise-spawn": "^1.3.2", + "lru-cache": "^6.0.0", + "mkdirp": "^1.0.4", + "npm-pick-manifest": "^6.1.1", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^2.0.2" + } + }, + "node_modules/yeoman-environment/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/yeoman-environment/node_modules/@npmcli/node-gyp": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-1.0.3.tgz", + "integrity": "sha512-fnkhw+fmX65kiLqk6E3BFLXNC26rUhK90zVwe2yncPliVT/Qos3xjhTLE59Df8KnPlcwIERXKVlU1bXoUQ+liA==", + "dev": true + }, + "node_modules/yeoman-environment/node_modules/@npmcli/promise-spawn": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-1.3.2.tgz", + "integrity": "sha512-QyAGYo/Fbj4MXeGdJcFzZ+FkDkomfRBrPM+9QYJSg+PxgAUL+LU3FneQk37rKR2/zjqkCV1BLHccX98wRXG3Sg==", + "dev": true, + "dependencies": { + "infer-owner": "^1.0.4" + } + }, + "node_modules/yeoman-environment/node_modules/@npmcli/run-script": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-2.0.0.tgz", + "integrity": "sha512-fSan/Pu11xS/TdaTpTB0MRn9guwGU8dye+x56mEVgBEd/QsybBbYcAL0phPXi8SGWFEChkQd6M9qL4y6VOpFig==", + "dev": true, + "dependencies": { + "@npmcli/node-gyp": "^1.0.2", + "@npmcli/promise-spawn": "^1.3.2", + "node-gyp": "^8.2.0", + "read-package-json-fast": "^2.0.1" + } + }, + "node_modules/yeoman-environment/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/yeoman-environment/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/yeoman-environment/node_modules/are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "dev": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yeoman-environment/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/yeoman-environment/node_modules/builtins": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz", + "integrity": "sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==", + "dev": true + }, + "node_modules/yeoman-environment/node_modules/cacache": { + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", + "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", + "dev": true, + "dependencies": { + "@npmcli/fs": "^1.0.0", + "@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/yeoman-environment/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "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/yeoman-environment/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/yeoman-environment/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==", + "dev": true + }, + "node_modules/yeoman-environment/node_modules/commander": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.1.0.tgz", + "integrity": "sha512-pRxBna3MJe6HKnBGsDyMv8ETbptw3axEdYHoqNh7gu5oDcew8fs0xnivZGm06Ogk8zGAJ9VX+OPEr2GXEQK4dg==", + "dev": true, + "engines": { + "node": ">= 10" + } + }, + "node_modules/yeoman-environment/node_modules/diff": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", + "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/yeoman-environment/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==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yeoman-environment/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/yeoman-environment/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yeoman-environment/node_modules/gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "dev": true, + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yeoman-environment/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/yeoman-environment/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/yeoman-environment/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/yeoman-environment/node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yeoman-environment/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/yeoman-environment/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/yeoman-environment/node_modules/ignore-walk": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-4.0.1.tgz", + "integrity": "sha512-rzDQLaW4jQbh2YrOFlJdCtX8qgJTehFRYiUB2r1osqTeDzV/3+Jh8fz1oAPzUThf3iku8Ds4IDqawI5d8mUiQw==", + "dev": true, + "dependencies": { + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yeoman-environment/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yeoman-environment/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==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yeoman-environment/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/yeoman-environment/node_modules/minipass-fetch": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", + "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", + "dev": true, + "dependencies": { + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "optionalDependencies": { + "encoding": "^0.1.12" + } + }, + "node_modules/yeoman-environment/node_modules/node-gyp": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", + "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", + "dev": true, + "dependencies": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^9.1.0", + "nopt": "^5.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": ">= 10.12.0" + } + }, + "node_modules/yeoman-environment/node_modules/node-gyp/node_modules/are-we-there-yet": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", + "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", + "dev": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/yeoman-environment/node_modules/node-gyp/node_modules/gauge": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", + "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "dev": true, + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/yeoman-environment/node_modules/node-gyp/node_modules/make-fetch-happen": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", + "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", + "dev": true, + "dependencies": { + "agentkeepalive": "^4.1.3", + "cacache": "^15.2.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.2", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^6.0.0", + "ssri": "^8.0.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/yeoman-environment/node_modules/node-gyp/node_modules/npmlog": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", + "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", + "dev": true, + "dependencies": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/yeoman-environment/node_modules/npm-install-checks": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-4.0.0.tgz", + "integrity": "sha512-09OmyDkNLYwqKPOnbI8exiOZU2GVVmQp7tgez2BPi5OZC8M82elDAps7sxC4l//uSUtotWqoEIDwjRvWH4qz8w==", + "dev": true, + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yeoman-environment/node_modules/npm-package-arg": { + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-8.1.5.tgz", + "integrity": "sha512-LhgZrg0n0VgvzVdSm1oiZworPbTxYHUJCgtsJW8mGvlDpxTM1vSJc3m5QZeUkhAHIzbz3VCHd/R4osi1L1Tg/Q==", + "dev": true, + "dependencies": { + "hosted-git-info": "^4.0.1", + "semver": "^7.3.4", + "validate-npm-package-name": "^3.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yeoman-environment/node_modules/npm-packlist": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-3.0.0.tgz", + "integrity": "sha512-L/cbzmutAwII5glUcf2DBRNY/d0TFd4e/FnaZigJV6JD85RHZXJFGwCndjMWiiViiWSsWt3tiOLpI3ByTnIdFQ==", + "dev": true, + "dependencies": { + "glob": "^7.1.6", + "ignore-walk": "^4.0.1", + "npm-bundled": "^1.1.1", + "npm-normalize-package-bin": "^1.0.1" + }, + "bin": { + "npm-packlist": "bin/index.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yeoman-environment/node_modules/npm-pick-manifest": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-6.1.1.tgz", + "integrity": "sha512-dBsdBtORT84S8V8UTad1WlUyKIY9iMsAmqxHbLdeEeBNMLQDlDWWra3wYUx9EBEIiG/YwAy0XyNHDd2goAsfuA==", + "dev": true, + "dependencies": { + "npm-install-checks": "^4.0.0", + "npm-normalize-package-bin": "^1.0.1", + "npm-package-arg": "^8.1.2", + "semver": "^7.3.4" + } + }, + "node_modules/yeoman-environment/node_modules/npm-registry-fetch": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-12.0.2.tgz", + "integrity": "sha512-Df5QT3RaJnXYuOwtXBXS9BWs+tHH2olvkCLh6jcR/b/u3DvPMlp3J0TvvYwplPKxHMOwfg287PYih9QqaVFoKA==", + "dev": true, + "dependencies": { + "make-fetch-happen": "^10.0.1", + "minipass": "^3.1.6", + "minipass-fetch": "^1.4.1", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.1.2", + "npm-package-arg": "^8.1.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16" + } + }, + "node_modules/yeoman-environment/node_modules/npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "dev": true, + "dependencies": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, + "node_modules/yeoman-environment/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yeoman-environment/node_modules/pacote": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-12.0.3.tgz", + "integrity": "sha512-CdYEl03JDrRO3x18uHjBYA9TyoW8gy+ThVcypcDkxPtKlw76e4ejhYB6i9lJ+/cebbjpqPW/CijjqxwDTts8Ow==", + "dev": true, + "dependencies": { + "@npmcli/git": "^2.1.0", + "@npmcli/installed-package-contents": "^1.0.6", + "@npmcli/promise-spawn": "^1.2.0", + "@npmcli/run-script": "^2.0.0", + "cacache": "^15.0.5", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "infer-owner": "^1.0.4", + "minipass": "^3.1.3", + "mkdirp": "^1.0.3", + "npm-package-arg": "^8.0.1", + "npm-packlist": "^3.0.0", + "npm-pick-manifest": "^6.0.0", + "npm-registry-fetch": "^12.0.0", + "promise-retry": "^2.0.1", + "read-package-json-fast": "^2.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.1.0" + }, + "bin": { + "pacote": "lib/bin.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16" + } + }, + "node_modules/yeoman-environment/node_modules/socks-proxy-agent": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz", + "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==", + "dev": true, + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/yeoman-environment/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/yeoman-environment/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yeoman-environment/node_modules/validate-npm-package-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz", + "integrity": "sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw==", + "dev": true, + "dependencies": { + "builtins": "^1.0.3" + } + }, + "node_modules/yeoman-generator": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/yeoman-generator/-/yeoman-generator-5.7.0.tgz", + "integrity": "sha512-z9ZwgKoDOd+llPDCwn8Ax2l4In5FMhlslxdeByW4AMxhT+HbTExXKEAahsClHSbwZz1i5OzRwLwRIUdOJBr5Bw==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "dargs": "^7.0.0", + "debug": "^4.1.1", + "execa": "^5.1.1", + "github-username": "^6.0.0", + "lodash": "^4.17.11", + "minimist": "^1.2.5", + "read-pkg-up": "^7.0.1", + "run-async": "^2.0.0", + "semver": "^7.2.1", + "shelljs": "^0.8.5", + "sort-keys": "^4.2.0", + "text-table": "^0.2.0" + }, + "engines": { + "node": ">=12.10.0" + }, + "peerDependencies": { + "yeoman-environment": "^3.2.0" + }, + "peerDependenciesMeta": { + "yeoman-environment": { + "optional": true + } + } + }, + "node_modules/yeoman-generator/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==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/yeoman-generator/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "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/yeoman-generator/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==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/yeoman-generator/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==", + "dev": true + }, + "node_modules/yeoman-generator/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/yeoman-generator/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/yeoman-generator/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==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/yeoman-generator/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/yeoman-generator/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==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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.6", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.11.6.tgz", + "integrity": "sha512-umJqFtKyZlPli669gB1gOrRE9hxUUGkZr7mo878z+NEBJZZixJkKeVYfnoLa7g25SseUDc92OZrMKKHySyJrFg==", + "dependencies": { + "tslib": "^2.3.0" + } + } + }, + "dependencies": { + "@adobe/css-tools": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.0.1.tgz", + "integrity": "sha512-+u76oB43nOHrF4DDWRLWDCtci7f3QJoEBigemIdIeTi1ODqjx6Tad9NCVnPRwewWlKkVab5PlK8DCtPTyX7S8g==", + "dev": true + }, + "@ampproject/remapping": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", + "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", + "requires": { + "@jridgewell/gen-mapping": "^0.1.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "@angular-builders/custom-webpack": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@angular-builders/custom-webpack/-/custom-webpack-14.1.0.tgz", + "integrity": "sha512-FLGDrBOg04cYvzCudeb15LWY2v91dtJ5+AfmP0aS/0T0D0AYmY4uM3FxZeh4jJcWETLvnHVFBCjan6y2Ct9J3A==", + "dev": true, + "requires": { + "@angular-devkit/architect": ">=0.1400.0 < 0.1500.0", + "@angular-devkit/build-angular": "^14.0.0", + "@angular-devkit/core": "^14.0.0", + "lodash": "^4.17.15", + "ts-node": "^10.0.0", + "tsconfig-paths": "^3.9.0", + "webpack-merge": "^5.7.3" + }, + "dependencies": { + "@angular-devkit/build-angular": { + "version": "14.2.11", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-14.2.11.tgz", + "integrity": "sha512-O3X7GXcCBCGceVSHT+GIJ2JrRCg2YcO7HtNavpmPrraNr1o+aCdTkmT5WTS2cqWkZBm/z0wqKR8PsX/ZoD2r1A==", + "dev": true, + "requires": { + "@ampproject/remapping": "2.2.0", + "@angular-devkit/architect": "0.1402.11", + "@angular-devkit/build-webpack": "0.1402.11", + "@angular-devkit/core": "14.2.11", + "@babel/core": "7.18.10", + "@babel/generator": "7.18.12", + "@babel/helper-annotate-as-pure": "7.18.6", + "@babel/plugin-proposal-async-generator-functions": "7.18.10", + "@babel/plugin-transform-async-to-generator": "7.18.6", + "@babel/plugin-transform-runtime": "7.18.10", + "@babel/preset-env": "7.18.10", + "@babel/runtime": "7.18.9", + "@babel/template": "7.18.10", + "@discoveryjs/json-ext": "0.5.7", + "@ngtools/webpack": "14.2.11", + "ansi-colors": "4.1.3", + "babel-loader": "8.2.5", + "babel-plugin-istanbul": "6.1.1", + "browserslist": "^4.9.1", + "cacache": "16.1.2", + "copy-webpack-plugin": "11.0.0", + "critters": "0.0.16", + "css-loader": "6.7.1", + "esbuild": "0.15.5", + "esbuild-wasm": "0.15.5", + "glob": "8.0.3", + "https-proxy-agent": "5.0.1", + "inquirer": "8.2.4", + "jsonc-parser": "3.1.0", + "karma-source-map-support": "1.4.0", + "less": "4.1.3", + "less-loader": "11.0.0", + "license-webpack-plugin": "4.0.2", + "loader-utils": "3.2.1", + "mini-css-extract-plugin": "2.6.1", + "minimatch": "5.1.0", + "open": "8.4.0", + "ora": "5.4.1", + "parse5-html-rewriting-stream": "6.0.1", + "piscina": "3.2.0", + "postcss": "8.4.16", + "postcss-import": "15.0.0", + "postcss-loader": "7.0.1", + "postcss-preset-env": "7.8.0", + "regenerator-runtime": "0.13.9", + "resolve-url-loader": "5.0.0", + "rxjs": "6.6.7", + "sass": "1.54.4", + "sass-loader": "13.0.2", + "semver": "7.3.7", + "source-map-loader": "4.0.0", + "source-map-support": "0.5.21", + "stylus": "0.59.0", + "stylus-loader": "7.0.0", + "terser": "5.14.2", + "text-table": "0.2.0", + "tree-kill": "1.2.2", + "tslib": "2.4.0", + "webpack": "5.74.0", + "webpack-dev-middleware": "5.3.3", + "webpack-dev-server": "4.11.0", + "webpack-merge": "5.8.0", + "webpack-subresource-integrity": "5.1.0" + } + }, + "@angular-devkit/build-webpack": { + "version": "0.1402.11", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1402.11.tgz", + "integrity": "sha512-Ajyg1O6B6JSHsDlPdh165uy3glW4IiUlRXu8VVAOSA88WIT1Dl17f4Oun0/t27ip0/CNceiVY9MzOqIwGL1E6g==", + "dev": true, + "requires": { + "@angular-devkit/architect": "0.1402.11", + "rxjs": "6.6.7" + } + }, + "@ngtools/webpack": { + "version": "14.2.11", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-14.2.11.tgz", + "integrity": "sha512-4enbLFAp98uTgWYF6OFceQqLcfv2/0brIrNN4iWT9xe/Mh3zdCt+eH42zvNRsqo9WXNWRSLvnx8I924p83LNlw==", + "dev": true, + "requires": {} + }, + "connect-history-api-fallback": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", + "dev": true + }, + "loader-utils": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.1.tgz", + "integrity": "sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==", + "dev": true + }, + "postcss-import": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.0.0.tgz", + "integrity": "sha512-Y20shPQ07RitgBGv2zvkEAu9bqvrD77C9axhj/aA1BQj4czape2MdClCExvB27EwYEJdGgKZBpKanb0t1rK2Kg==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + } + }, + "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": "4.0.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.1.tgz", + "integrity": "sha512-lELhBAAly9NowEsX0yZBlw9ahZG+sK/1RJ21EpzdYHKEs13Vku3LJ+MIPhh4sMs0oCCeufZQEQbMekiA4vuVIQ==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + } + }, + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "dev": true + }, + "webpack-dev-server": { + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.11.0.tgz", + "integrity": "sha512-L5S4Q2zT57SK7tazgzjMiSMBdsw+rGYIX27MgPgx7LDhWO0lViPrHKoLS7jo5In06PWYAhlYu3PbyoC6yAThbw==", + "dev": true, + "requires": { + "@types/bonjour": "^3.5.9", + "@types/connect-history-api-fallback": "^1.3.5", + "@types/express": "^4.17.13", + "@types/serve-index": "^1.9.1", + "@types/serve-static": "^1.13.10", + "@types/sockjs": "^0.3.33", + "@types/ws": "^8.5.1", + "ansi-html-community": "^0.0.8", + "bonjour-service": "^1.0.11", + "chokidar": "^3.5.3", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^2.0.0", + "default-gateway": "^6.0.3", + "express": "^4.17.3", + "graceful-fs": "^4.2.6", + "html-entities": "^2.3.2", + "http-proxy-middleware": "^2.0.3", + "ipaddr.js": "^2.0.1", + "open": "^8.0.9", + "p-retry": "^4.5.0", + "rimraf": "^3.0.2", + "schema-utils": "^4.0.0", + "selfsigned": "^2.0.1", + "serve-index": "^1.9.1", + "sockjs": "^0.3.24", + "spdy": "^4.0.2", + "webpack-dev-middleware": "^5.3.1", + "ws": "^8.4.2" + } + }, + "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" + } + } + } + }, + "@angular-builders/jest": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@angular-builders/jest/-/jest-14.1.0.tgz", + "integrity": "sha512-uGXyJ40F7HXfoWwW1EK5U75sUUQ4zVireLYPUYWT1/t1vqizss0mIPR026bMWwScWJhetdopxlWcql4wfNuXyQ==", + "dev": true, + "requires": { + "@angular-devkit/architect": ">=0.1400.0 < 0.1500.0", + "@angular-devkit/core": "^14.0.0", + "jest-preset-angular": "12.2.2", + "lodash": "^4.17.15" + } + }, + "@angular-devkit/architect": { + "version": "0.1402.11", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1402.11.tgz", + "integrity": "sha512-RuSZrBQ+QbipAESZ4aXCyAMQHaEaDyyV/FDS9J2HJWfEFbRD5oxlEt/tBC8XjmJQsktaUOh07GT8MNJjPKVAQw==", + "dev": true, + "requires": { + "@angular-devkit/core": "14.2.11", + "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": "14.2.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-14.2.1.tgz", + "integrity": "sha512-6FeIe2nsNH/fslZmTfbo+RCjLk3HuCQa5rb/SFf3w0mwzWCrhV0/SqnQ4+YYgkKrohmTX+q5JmJPwWnkqgdIgQ==", + "dev": true, + "requires": { + "@ampproject/remapping": "2.2.0", + "@angular-devkit/architect": "0.1402.1", + "@angular-devkit/build-webpack": "0.1402.1", + "@angular-devkit/core": "14.2.1", + "@babel/core": "7.18.10", + "@babel/generator": "7.18.12", + "@babel/helper-annotate-as-pure": "7.18.6", + "@babel/plugin-proposal-async-generator-functions": "7.18.10", + "@babel/plugin-transform-async-to-generator": "7.18.6", + "@babel/plugin-transform-runtime": "7.18.10", + "@babel/preset-env": "7.18.10", + "@babel/runtime": "7.18.9", + "@babel/template": "7.18.10", + "@discoveryjs/json-ext": "0.5.7", + "@ngtools/webpack": "14.2.1", + "ansi-colors": "4.1.3", + "babel-loader": "8.2.5", + "babel-plugin-istanbul": "6.1.1", + "browserslist": "^4.9.1", + "cacache": "16.1.2", + "copy-webpack-plugin": "11.0.0", + "critters": "0.0.16", + "css-loader": "6.7.1", + "esbuild": "0.15.5", + "esbuild-wasm": "0.15.5", + "glob": "8.0.3", + "https-proxy-agent": "5.0.1", + "inquirer": "8.2.4", + "jsonc-parser": "3.1.0", + "karma-source-map-support": "1.4.0", + "less": "4.1.3", + "less-loader": "11.0.0", + "license-webpack-plugin": "4.0.2", + "loader-utils": "3.2.0", + "mini-css-extract-plugin": "2.6.1", + "minimatch": "5.1.0", + "open": "8.4.0", + "ora": "5.4.1", + "parse5-html-rewriting-stream": "6.0.1", + "piscina": "3.2.0", + "postcss": "8.4.16", + "postcss-import": "14.1.0", + "postcss-loader": "7.0.1", + "postcss-preset-env": "7.8.0", + "regenerator-runtime": "0.13.9", + "resolve-url-loader": "5.0.0", + "rxjs": "6.6.7", + "sass": "1.54.4", + "sass-loader": "13.0.2", + "semver": "7.3.7", + "source-map-loader": "4.0.0", + "source-map-support": "0.5.21", + "stylus": "0.59.0", + "stylus-loader": "7.0.0", + "terser": "5.14.2", + "text-table": "0.2.0", + "tree-kill": "1.2.2", + "tslib": "2.4.0", + "webpack": "5.74.0", + "webpack-dev-middleware": "5.3.3", + "webpack-dev-server": "4.10.0", + "webpack-merge": "5.8.0", + "webpack-subresource-integrity": "5.1.0" + }, + "dependencies": { + "@angular-devkit/architect": { + "version": "0.1402.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1402.1.tgz", + "integrity": "sha512-OZ5mkVHSrk5WX6wIfvkxJUCqJRtfQh1S476qHIi80llhJufFTwoOwJrcz7XtNRvbZMLRMztf0aIhEDaUtEBRZQ==", + "dev": true, + "requires": { + "@angular-devkit/core": "14.2.1", + "rxjs": "6.6.7" + } + }, + "@angular-devkit/core": { + "version": "14.2.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-14.2.1.tgz", + "integrity": "sha512-lW8oNGuJqr4r31FWBjfWQYkSXdiOHBGOThIEtHvUVBKfPF/oVrupLueCUgBPel+NvxENXdo93uPsqHN7bZbmsQ==", + "dev": true, + "requires": { + "ajv": "8.11.0", + "ajv-formats": "2.1.1", + "jsonc-parser": "3.1.0", + "rxjs": "6.6.7", + "source-map": "0.7.4" + } + }, + "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 + } + } + }, + "tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "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" + } + } + } + }, + "@angular-devkit/build-webpack": { + "version": "0.1402.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1402.1.tgz", + "integrity": "sha512-S9WvNKrHVgOWNqGCytJNPBEMojB9j6cgl5XlUMEQ+4NV5geOlDNiZltQ/gbC1jz19OU5N+Ky374mPI5IeIBqZg==", + "dev": true, + "requires": { + "@angular-devkit/architect": "0.1402.1", + "rxjs": "6.6.7" + }, + "dependencies": { + "@angular-devkit/architect": { + "version": "0.1402.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1402.1.tgz", + "integrity": "sha512-OZ5mkVHSrk5WX6wIfvkxJUCqJRtfQh1S476qHIi80llhJufFTwoOwJrcz7XtNRvbZMLRMztf0aIhEDaUtEBRZQ==", + "dev": true, + "requires": { + "@angular-devkit/core": "14.2.1", + "rxjs": "6.6.7" + } + }, + "@angular-devkit/core": { + "version": "14.2.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-14.2.1.tgz", + "integrity": "sha512-lW8oNGuJqr4r31FWBjfWQYkSXdiOHBGOThIEtHvUVBKfPF/oVrupLueCUgBPel+NvxENXdo93uPsqHN7bZbmsQ==", + "dev": true, + "requires": { + "ajv": "8.11.0", + "ajv-formats": "2.1.1", + "jsonc-parser": "3.1.0", + "rxjs": "6.6.7", + "source-map": "0.7.4" + } + }, + "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": "14.2.11", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-14.2.11.tgz", + "integrity": "sha512-cBIGs6y9rykOQqnuAQOB1DgIRyBFYtvKRJb7QNUfIJ0qUfARKkuV/yikv3lrb95ePGkmoRzmjkFqcFZiYU+r7A==", + "dev": true, + "requires": { + "ajv": "8.11.0", + "ajv-formats": "2.1.1", + "jsonc-parser": "3.1.0", + "rxjs": "6.6.7", + "source-map": "0.7.4" + }, + "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/schematics": { + "version": "14.2.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-14.2.1.tgz", + "integrity": "sha512-0U18FwDYt4zROBPrvewH6iBTkf2ozVHN4/gxUb9jWrqVw8mPU5AWc/iYxQLHBSinkr2Egjo1H/i9aBqgJSeh3g==", + "dev": true, + "requires": { + "@angular-devkit/core": "14.2.1", + "jsonc-parser": "3.1.0", + "magic-string": "0.26.2", + "ora": "5.4.1", + "rxjs": "6.6.7" + }, + "dependencies": { + "@angular-devkit/core": { + "version": "14.2.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-14.2.1.tgz", + "integrity": "sha512-lW8oNGuJqr4r31FWBjfWQYkSXdiOHBGOThIEtHvUVBKfPF/oVrupLueCUgBPel+NvxENXdo93uPsqHN7bZbmsQ==", + "dev": true, + "requires": { + "ajv": "8.11.0", + "ajv-formats": "2.1.1", + "jsonc-parser": "3.1.0", + "rxjs": "6.6.7", + "source-map": "0.7.4" + } + }, + "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-eslint/bundled-angular-compiler": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-14.0.3.tgz", + "integrity": "sha512-1JMdb5IrgFH+bxY8ZYXyNyu31+BELbahGKOi+jX0ZyZgGA8nbukzjL+QVNwwaw1RNuyiCxZzCwA/tRSGJMqSYA==", + "dev": true + }, + "@angular-eslint/eslint-plugin": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin/-/eslint-plugin-14.0.3.tgz", + "integrity": "sha512-s3rgibA+sFcDswpKUXbdykOBT7KoSk205bIBARJrIZr0zR7zsPLS/XQQGk83VICeAgcGHSPlNBmspOBrLfihEQ==", + "dev": true, + "requires": { + "@angular-eslint/utils": "14.0.3", + "@typescript-eslint/utils": "5.29.0" + } + }, + "@angular-eslint/utils": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/@angular-eslint/utils/-/utils-14.0.3.tgz", + "integrity": "sha512-horyyq6shximqacDhHm6GhEJI71dvGfr2iiqP/PtT944dhY+Ml6fj85H9ywKQxd5ENYPBKrzOGCBxpLyQU07Ow==", + "dev": true, + "requires": { + "@angular-eslint/bundled-angular-compiler": "14.0.3", + "@typescript-eslint/utils": "5.29.0" + } + }, + "@angular/animations": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-14.2.0.tgz", + "integrity": "sha512-T54F5NvX0XMj6L7hdqYYORFjY6EckOWaK96bF9QqJLKwIRViCEGR5YzaHGhQaAipvUJ+kEwN+bnRBJWOaNbmUw==", + "requires": { + "tslib": "^2.3.0" + } + }, + "@angular/cli": { + "version": "14.2.1", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-14.2.1.tgz", + "integrity": "sha512-ab/kpK3wYQvDOdhwfk3cVSiLMyl9lEVrNiwvrTnPLdt3jwqkT5Gm28WFShnOuNCaKea3iHP7LIItoRxeIWQQ9A==", + "dev": true, + "requires": { + "@angular-devkit/architect": "0.1402.1", + "@angular-devkit/core": "14.2.1", + "@angular-devkit/schematics": "14.2.1", + "@schematics/angular": "14.2.1", + "@yarnpkg/lockfile": "1.1.0", + "ansi-colors": "4.1.3", + "debug": "4.3.4", + "ini": "3.0.0", + "inquirer": "8.2.4", + "jsonc-parser": "3.1.0", + "npm-package-arg": "9.1.0", + "npm-pick-manifest": "7.0.1", + "open": "8.4.0", + "ora": "5.4.1", + "pacote": "13.6.2", + "resolve": "1.22.1", + "semver": "7.3.7", + "symbol-observable": "4.0.0", + "uuid": "8.3.2", + "yargs": "17.5.1" + }, + "dependencies": { + "@angular-devkit/architect": { + "version": "0.1402.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1402.1.tgz", + "integrity": "sha512-OZ5mkVHSrk5WX6wIfvkxJUCqJRtfQh1S476qHIi80llhJufFTwoOwJrcz7XtNRvbZMLRMztf0aIhEDaUtEBRZQ==", + "dev": true, + "requires": { + "@angular-devkit/core": "14.2.1", + "rxjs": "6.6.7" + } + }, + "@angular-devkit/core": { + "version": "14.2.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-14.2.1.tgz", + "integrity": "sha512-lW8oNGuJqr4r31FWBjfWQYkSXdiOHBGOThIEtHvUVBKfPF/oVrupLueCUgBPel+NvxENXdo93uPsqHN7bZbmsQ==", + "dev": true, + "requires": { + "ajv": "8.11.0", + "ajv-formats": "2.1.1", + "jsonc-parser": "3.1.0", + "rxjs": "6.6.7", + "source-map": "0.7.4" + } + }, + "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/common": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@angular/common/-/common-14.2.0.tgz", + "integrity": "sha512-dXCGuM/Yh9B8OVVHHfPuXLYdu0olemKmuYLZXGoqyzeYU5OpQ6+qcHYTrlREkWpF9QY+pk2ewfM9Hke1UENEHA==", + "requires": { + "tslib": "^2.3.0" + } + }, + "@angular/compiler": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-14.2.0.tgz", + "integrity": "sha512-csnvopy5a6ctdT7sHgwgn3wRni+UIk2zvs7WYsx+gtLTdH7SPg78CN0XZR/KQ142qLtuMnmkVON3D3ejmqC9xg==", + "requires": { + "tslib": "^2.3.0" + } + }, + "@angular/compiler-cli": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@angular/compiler-cli/-/compiler-cli-14.2.0.tgz", + "integrity": "sha512-cMjMVmAitY4GIn45pIqzhy4GKWfVAJSktCtuAebaAYC6Nqo9LbvqF2dnHK0MHoqjAf98e5zmz9WeBOQ7YlRZFQ==", + "requires": { + "@babel/core": "^7.17.2", + "chokidar": "^3.0.0", + "convert-source-map": "^1.5.1", + "dependency-graph": "^0.11.0", + "magic-string": "^0.26.0", + "reflect-metadata": "^0.1.2", + "semver": "^7.0.0", + "sourcemap-codec": "^1.4.8", + "tslib": "^2.3.0", + "yargs": "^17.2.1" + } + }, + "@angular/core": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-14.2.0.tgz", + "integrity": "sha512-m00WZL+df90QjrNmA43W8oSP1hyePoEXgw36JcFLQ+2ISO1HvqoeuKXIccyIQKpCuQ9VLNv5ptlTio6pjmcCqg==", + "requires": { + "tslib": "^2.3.0" + } + }, + "@angular/forms": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-14.2.0.tgz", + "integrity": "sha512-KVAd4ITEUnP/MLNTDbTWEMoYIZgKawvoaNu4nBP0DAcZsADmB5a6IPYcg6GrgJ+teN0SnpiCxKvq1hRITj3d7g==", + "requires": { + "tslib": "^2.3.0" + } + }, + "@angular/localize": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@angular/localize/-/localize-14.2.0.tgz", + "integrity": "sha512-sV+4itOllkgYkHMMtfYGsOvO+nuFMBlk6RKFVqWZcFO+XgOC4E9bXhRbkpGZ0JcIr42tlB63krjopnYfN9u+7A==", + "requires": { + "@babel/core": "7.18.9", + "glob": "8.0.3", + "yargs": "^17.2.1" + }, + "dependencies": { + "@babel/core": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.9.tgz", + "integrity": "sha512-1LIb1eL8APMy91/IMW+31ckrfBM4yCoLaVzoDhZUKSM4cu1L1nIidyxkCgzPAgrC5WEz36IPEr/eSeSF9pIn+g==", + "requires": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.18.9", + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-module-transforms": "^7.18.9", + "@babel/helpers": "^7.18.9", + "@babel/parser": "^7.18.9", + "@babel/template": "^7.18.6", + "@babel/traverse": "^7.18.9", + "@babel/types": "^7.18.9", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.1", + "semver": "^6.3.0" + } + }, + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + } + } + }, + "@angular/platform-browser": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-14.2.0.tgz", + "integrity": "sha512-zYWB9FtZQZEhPV2PuzaoXWChWS+7o4SP+0sF8hLddGNOezf1FsKKrqDhyYtQYgZ2qQPsS+uDFfkVry5dppALUQ==", + "requires": { + "tslib": "^2.3.0" + } + }, + "@angular/platform-browser-dynamic": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-14.2.0.tgz", + "integrity": "sha512-CRSWbJiohfz4KBU4ts2kq2B2lATApMeG6uuxgXwNJAEUkn63EYbrFVY9JTaApyZ7X+3dUDZZtcsfNmQruK2UxQ==", + "requires": { + "tslib": "^2.3.0" + } + }, + "@angular/router": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@angular/router/-/router-14.2.0.tgz", + "integrity": "sha512-SvtmSlRCIWeLhbXYh09W7luPKTtpZL16PB4DbT3VVfZlt2PfMys/tAmOCvChNxjkOgV7yEGaqklWw/1wKxeRlg==", + "requires": { + "tslib": "^2.3.0" + } + }, + "@angular/service-worker": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/@angular/service-worker/-/service-worker-14.2.0.tgz", + "integrity": "sha512-9lX+YZyeO02nOUkhbHo/S2YardiVVpmTr4UKa2v7uQUauASOAh1haPDBQpHyiEN6TIXMMJ0bawiDPFcNPrvU8g==", + "dev": true, + "requires": { + "tslib": "^2.3.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.18.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", + "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "requires": { + "@babel/highlight": "^7.18.6" + } + }, + "@babel/compat-data": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.19.0.tgz", + "integrity": "sha512-y5rqgTTPTmaF5e2nVhOxw+Ur9HDJLsWb6U/KpgUzRZEdPfE6VOubXBKLdbcUTijzRptednSBDQbYZBOSqJxpJw==" + }, + "@babel/core": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.10.tgz", + "integrity": "sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw==", + "requires": { + "@ampproject/remapping": "^2.1.0", + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.18.10", + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-module-transforms": "^7.18.9", + "@babel/helpers": "^7.18.9", + "@babel/parser": "^7.18.10", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.18.10", + "@babel/types": "^7.18.10", + "convert-source-map": "^1.7.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.1", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + } + } + }, + "@babel/generator": { + "version": "7.18.12", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.18.12.tgz", + "integrity": "sha512-dfQ8ebCN98SvyL7IxNMCUtZQSq5R7kxgN+r8qYTGDmmSion1hX2C0zq2yo1bsCDhXixokv1SAWTZUMYbO/V5zg==", + "requires": { + "@babel/types": "^7.18.10", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" + }, + "dependencies": { + "@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + } + } + }, + "@babel/helper-annotate-as-pure": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.18.6.tgz", + "integrity": "sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-builder-binary-assignment-operator-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.18.9.tgz", + "integrity": "sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw==", + "dev": true, + "requires": { + "@babel/helper-explode-assignable-expression": "^7.18.6", + "@babel/types": "^7.18.9" + } + }, + "@babel/helper-compilation-targets": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.0.tgz", + "integrity": "sha512-Ai5bNWXIvwDvWM7njqsG3feMlL9hCVQsPYXodsZyLwshYkZVJt59Gftau4VrE8S9IT9asd2uSP1hG6wCNw+sXA==", + "requires": { + "@babel/compat-data": "^7.19.0", + "@babel/helper-validator-option": "^7.18.6", + "browserslist": "^4.20.2", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + } + } + }, + "@babel/helper-create-class-features-plugin": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.19.0.tgz", + "integrity": "sha512-NRz8DwF4jT3UfrmUoZjd0Uph9HQnP30t7Ash+weACcyNkiYTywpIjDBgReJMKgr+n86sn2nPVVmJ28Dm053Kqw==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-member-expression-to-functions": "^7.18.9", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-replace-supers": "^7.18.9", + "@babel/helper-split-export-declaration": "^7.18.6" + } + }, + "@babel/helper-create-regexp-features-plugin": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.19.0.tgz", + "integrity": "sha512-htnV+mHX32DF81amCDrwIDr8nrp1PTm+3wfBN9/v8QJOLEioOCOG7qNyq0nHeFiWbT3Eb7gsPwEmV64UCQ1jzw==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "regexpu-core": "^5.1.0" + } + }, + "@babel/helper-define-polyfill-provider": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@babel/helper-define-polyfill-provider/-/helper-define-polyfill-provider-0.3.2.tgz", + "integrity": "sha512-r9QJJ+uDWrd+94BSPcP6/de67ygLtvVy6cK4luE6MOuDsZIdoaPBnfSpbO/+LTifjPckbKXRuI9BB/Z2/y3iTg==", + "dev": true, + "requires": { + "@babel/helper-compilation-targets": "^7.17.7", + "@babel/helper-plugin-utils": "^7.16.7", + "debug": "^4.1.1", + "lodash.debounce": "^4.0.8", + "resolve": "^1.14.2", + "semver": "^6.1.2" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true + } + } + }, + "@babel/helper-environment-visitor": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", + "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==" + }, + "@babel/helper-explode-assignable-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.18.6.tgz", + "integrity": "sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-function-name": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", + "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", + "requires": { + "@babel/template": "^7.18.10", + "@babel/types": "^7.19.0" + } + }, + "@babel/helper-hoist-variables": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", + "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-member-expression-to-functions": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.18.9.tgz", + "integrity": "sha512-RxifAh2ZoVU67PyKIO4AMi1wTenGfMR/O/ae0CCRqwgBAt5v7xjdtRw7UoSbsreKrQn5t7r89eruK/9JjYHuDg==", + "dev": true, + "requires": { + "@babel/types": "^7.18.9" + } + }, + "@babel/helper-module-imports": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-module-transforms": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.19.0.tgz", + "integrity": "sha512-3HBZ377Fe14RbLIA+ac3sY4PTgpxHVkFrESaWhoI5PuyXPBBX8+C34qblV9G89ZtycGJCmCI/Ut+VUDK4bltNQ==", + "requires": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-simple-access": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/helper-validator-identifier": "^7.18.6", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.19.0", + "@babel/types": "^7.19.0" + } + }, + "@babel/helper-optimise-call-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.18.6.tgz", + "integrity": "sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==", + "dev": true, + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.19.0.tgz", + "integrity": "sha512-40Ryx7I8mT+0gaNxm8JGTZFUITNqdLAgdg0hXzeVZxVD6nFsdhQvip6v8dqkRHzsz1VFpFAaOCHNn0vKBL7Czw==", + "dev": true + }, + "@babel/helper-remap-async-to-generator": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.18.9.tgz", + "integrity": "sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-wrap-function": "^7.18.9", + "@babel/types": "^7.18.9" + } + }, + "@babel/helper-replace-supers": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.18.9.tgz", + "integrity": "sha512-dNsWibVI4lNT6HiuOIBr1oyxo40HvIVmbwPUm3XZ7wMh4k2WxrxTqZwSqw/eEmXDS9np0ey5M2bz9tBmO9c+YQ==", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-member-expression-to-functions": "^7.18.9", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/traverse": "^7.18.9", + "@babel/types": "^7.18.9" + } + }, + "@babel/helper-simple-access": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz", + "integrity": "sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==", + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.18.9.tgz", + "integrity": "sha512-imytd2gHi3cJPsybLRbmFrF7u5BIEuI2cNheyKi3/iOBC63kNn3q8Crn2xVuESli0aM4KYsyEqKyS7lFL8YVtw==", + "dev": true, + "requires": { + "@babel/types": "^7.18.9" + } + }, + "@babel/helper-split-export-declaration": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", + "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "requires": { + "@babel/types": "^7.18.6" + } + }, + "@babel/helper-string-parser": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz", + "integrity": "sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==" + }, + "@babel/helper-validator-identifier": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz", + "integrity": "sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g==" + }, + "@babel/helper-validator-option": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", + "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==" + }, + "@babel/helper-wrap-function": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helper-wrap-function/-/helper-wrap-function-7.19.0.tgz", + "integrity": "sha512-txX8aN8CZyYGTwcLhlk87KRqncAzhh5TpQamZUa0/u3an36NtDpUP6bQgBCBcLeBs09R/OwQu3OjK0k/HwfNDg==", + "dev": true, + "requires": { + "@babel/helper-function-name": "^7.19.0", + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.19.0", + "@babel/types": "^7.19.0" + } + }, + "@babel/helpers": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.19.0.tgz", + "integrity": "sha512-DRBCKGwIEdqY3+rPJgG/dKfQy9+08rHIAJx8q2p+HSWP87s2HCrQmaAMMyMll2kIXKCW0cO1RdQskx15Xakftg==", + "requires": { + "@babel/template": "^7.18.10", + "@babel/traverse": "^7.19.0", + "@babel/types": "^7.19.0" + } + }, + "@babel/highlight": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", + "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "requires": { + "@babel/helper-validator-identifier": "^7.18.6", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@babel/parser": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.19.0.tgz", + "integrity": "sha512-74bEXKX2h+8rrfQUfsBfuZZHzsEs6Eql4pqy/T4Nn6Y9wNPggQOqD6z6pn5Bl8ZfysKouFZT/UXEH94ummEeQw==" + }, + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.18.6.tgz", + "integrity": "sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining/-/plugin-bugfix-v8-spread-parameters-in-optional-chaining-7.18.9.tgz", + "integrity": "sha512-AHrP9jadvH7qlOj6PINbgSuphjQUAK7AOT7DPjBo9EHoLhQTnnK5u45e1Hd4DbSQEO9nqPWtQ89r+XEOWFScKg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", + "@babel/plugin-proposal-optional-chaining": "^7.18.9" + } + }, + "@babel/plugin-proposal-async-generator-functions": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.18.10.tgz", + "integrity": "sha512-1mFuY2TOsR1hxbjCo4QL+qlIjV07p4H4EUYw2J/WCqsvFV6V9X9z9YhXbWndc/4fw+hYGlDT7egYxliMp5O6Ew==", + "dev": true, + "requires": { + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-remap-async-to-generator": "^7.18.9", + "@babel/plugin-syntax-async-generators": "^7.8.4" + } + }, + "@babel/plugin-proposal-class-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", + "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-proposal-class-static-block": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-static-block/-/plugin-proposal-class-static-block-7.18.6.tgz", + "integrity": "sha512-+I3oIiNxrCpup3Gi8n5IGMwj0gOCAjcJUSQEcotNnCCPMEnixawOQ+KeJPlgfjzx+FKQ1QSyZOWe7wmoJp7vhw==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-class-static-block": "^7.14.5" + } + }, + "@babel/plugin-proposal-dynamic-import": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.18.6.tgz", + "integrity": "sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-dynamic-import": "^7.8.3" + } + }, + "@babel/plugin-proposal-export-namespace-from": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.18.9.tgz", + "integrity": "sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-export-namespace-from": "^7.8.3" + } + }, + "@babel/plugin-proposal-json-strings": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.18.6.tgz", + "integrity": "sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-json-strings": "^7.8.3" + } + }, + "@babel/plugin-proposal-logical-assignment-operators": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.18.9.tgz", + "integrity": "sha512-128YbMpjCrP35IOExw2Fq+x55LMP42DzhOhX2aNNIdI9avSWl2PI0yuBWarr3RYpZBSPtabfadkH2yeRiMD61Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4" + } + }, + "@babel/plugin-proposal-nullish-coalescing-operator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.18.6.tgz", + "integrity": "sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3" + } + }, + "@babel/plugin-proposal-numeric-separator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.18.6.tgz", + "integrity": "sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-numeric-separator": "^7.10.4" + } + }, + "@babel/plugin-proposal-object-rest-spread": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.18.9.tgz", + "integrity": "sha512-kDDHQ5rflIeY5xl69CEqGEZ0KY369ehsCIEbTGb4siHG5BE9sga/T0r0OUwyZNLMmZE79E1kbsqAjwFCW4ds6Q==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.18.8", + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-transform-parameters": "^7.18.8" + } + }, + "@babel/plugin-proposal-optional-catch-binding": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.18.6.tgz", + "integrity": "sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3" + } + }, + "@babel/plugin-proposal-optional-chaining": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.18.9.tgz", + "integrity": "sha512-v5nwt4IqBXihxGsW2QmCWMDS3B3bzGIk/EQVZz2ei7f3NJl8NzAJVvUmpDW5q1CRNY+Beb/k58UAH1Km1N411w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9", + "@babel/plugin-syntax-optional-chaining": "^7.8.3" + } + }, + "@babel/plugin-proposal-private-methods": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.18.6.tgz", + "integrity": "sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==", + "dev": true, + "requires": { + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-proposal-private-property-in-object": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.18.6.tgz", + "integrity": "sha512-9Rysx7FOctvT5ouj5JODjAFAkgGoudQuLPamZb0v1TGLpapdNaftzifU8NTWQm0IRjqoYypdrSmyWgkocDQ8Dw==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5" + } + }, + "@babel/plugin-proposal-unicode-property-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz", + "integrity": "sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@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-assertions": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-assertions/-/plugin-syntax-import-assertions-7.18.6.tgz", + "integrity": "sha512-/DU3RXad9+bZwrgWJQKbr39gYbJpLJHezqEzRzi/BHRlJ9zsQb4CK2CA/5apllXNomwA1qHwzvHl+AdEmC5krQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@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.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.18.6.tgz", + "integrity": "sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-arrow-functions": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.18.6.tgz", + "integrity": "sha512-9S9X9RUefzrsHZmKMbDXxweEH+YlE8JJEuat9FdvW9Qh1cw7W64jELCtWNkPBPX5En45uy28KGvA/AySqUh8CQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-async-to-generator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.18.6.tgz", + "integrity": "sha512-ARE5wZLKnTgPW7/1ftQmSi1CmkqqHo2DNmtztFhvgtOWSDfq0Cq9/9L+KnZNYSNrydBekhW3rwShduf59RoXag==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-remap-async-to-generator": "^7.18.6" + } + }, + "@babel/plugin-transform-block-scoped-functions": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.18.6.tgz", + "integrity": "sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-block-scoping": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.18.9.tgz", + "integrity": "sha512-5sDIJRV1KtQVEbt/EIBwGy4T01uYIo4KRB3VUqzkhrAIOGx7AoctL9+Ux88btY0zXdDyPJ9mW+bg+v+XEkGmtw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-classes": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-classes/-/plugin-transform-classes-7.19.0.tgz", + "integrity": "sha512-YfeEE9kCjqTS9IitkgfJuxjcEtLUHMqa8yUJ6zdz8vR7hKuo6mOy2C05P0F1tdMmDCeuyidKnlrw/iTppHcr2A==", + "dev": true, + "requires": { + "@babel/helper-annotate-as-pure": "^7.18.6", + "@babel/helper-compilation-targets": "^7.19.0", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-optimise-call-expression": "^7.18.6", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-replace-supers": "^7.18.9", + "@babel/helper-split-export-declaration": "^7.18.6", + "globals": "^11.1.0" + } + }, + "@babel/plugin-transform-computed-properties": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.18.9.tgz", + "integrity": "sha512-+i0ZU1bCDymKakLxn5srGHrsAPRELC2WIbzwjLhHW9SIE1cPYkLCL0NlnXMZaM1vhfgA2+M7hySk42VBvrkBRw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-destructuring": { + "version": "7.18.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.18.13.tgz", + "integrity": "sha512-TodpQ29XekIsex2A+YJPj5ax2plkGa8YYY6mFjCohk/IG9IY42Rtuj1FuDeemfg2ipxIFLzPeA83SIBnlhSIow==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-dotall-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.18.6.tgz", + "integrity": "sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-duplicate-keys": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.18.9.tgz", + "integrity": "sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-exponentiation-operator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.18.6.tgz", + "integrity": "sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==", + "dev": true, + "requires": { + "@babel/helper-builder-binary-assignment-operator-visitor": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-for-of": { + "version": "7.18.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.18.8.tgz", + "integrity": "sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-function-name": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.18.9.tgz", + "integrity": "sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==", + "dev": true, + "requires": { + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-function-name": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-literals": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-literals/-/plugin-transform-literals-7.18.9.tgz", + "integrity": "sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-member-expression-literals": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.18.6.tgz", + "integrity": "sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-modules-amd": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.18.6.tgz", + "integrity": "sha512-Pra5aXsmTsOnjM3IajS8rTaLCy++nGM4v3YR4esk5PCsyg9z8NA5oQLwxzMUtDBd8F+UmVza3VxoAaWCbzH1rg==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-commonjs": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.18.6.tgz", + "integrity": "sha512-Qfv2ZOWikpvmedXQJDSbxNqy7Xr/j2Y8/KfijM0iJyKkBTmWuvCA1yeH1yDM7NJhBW/2aXxeucLj6i80/LAJ/Q==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-simple-access": "^7.18.6", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-systemjs": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.19.0.tgz", + "integrity": "sha512-x9aiR0WXAWmOWsqcsnrzGR+ieaTMVyGyffPVA7F8cXAGt/UxefYv6uSHZLkAFChN5M5Iy1+wjE+xJuPt22H39A==", + "dev": true, + "requires": { + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-module-transforms": "^7.19.0", + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-validator-identifier": "^7.18.6", + "babel-plugin-dynamic-import-node": "^2.3.3" + } + }, + "@babel/plugin-transform-modules-umd": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.18.6.tgz", + "integrity": "sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==", + "dev": true, + "requires": { + "@babel/helper-module-transforms": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-named-capturing-groups-regex": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.19.0.tgz", + "integrity": "sha512-HDSuqOQzkU//kfGdiHBt71/hkDTApw4U/cMVgKgX7PqfB3LOaK+2GtCEsBu1dL9CkswDm0Gwehht1dCr421ULQ==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.19.0", + "@babel/helper-plugin-utils": "^7.19.0" + } + }, + "@babel/plugin-transform-new-target": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.18.6.tgz", + "integrity": "sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-object-super": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.18.6.tgz", + "integrity": "sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "@babel/helper-replace-supers": "^7.18.6" + } + }, + "@babel/plugin-transform-parameters": { + "version": "7.18.8", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.18.8.tgz", + "integrity": "sha512-ivfbE3X2Ss+Fj8nnXvKJS6sjRG4gzwPMsP+taZC+ZzEGjAYlvENixmt1sZ5Ca6tWls+BlKSGKPJ6OOXvXCbkFg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-property-literals": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.18.6.tgz", + "integrity": "sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-regenerator": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.18.6.tgz", + "integrity": "sha512-poqRI2+qiSdeldcz4wTSTXBRryoq3Gc70ye7m7UD5Ww0nE29IXqMl6r7Nd15WBgRd74vloEMlShtH6CKxVzfmQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6", + "regenerator-transform": "^0.15.0" + } + }, + "@babel/plugin-transform-reserved-words": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.18.6.tgz", + "integrity": "sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-runtime": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.18.10.tgz", + "integrity": "sha512-q5mMeYAdfEbpBAgzl7tBre/la3LeCxmDO1+wMXRdPWbcoMjR3GiXlCLk7JBZVVye0bqTGNMbt0yYVXX1B1jEWQ==", + "dev": true, + "requires": { + "@babel/helper-module-imports": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.9", + "babel-plugin-polyfill-corejs2": "^0.3.2", + "babel-plugin-polyfill-corejs3": "^0.5.3", + "babel-plugin-polyfill-regenerator": "^0.4.0", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true + } + } + }, + "@babel/plugin-transform-shorthand-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.18.6.tgz", + "integrity": "sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-spread": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-spread/-/plugin-transform-spread-7.19.0.tgz", + "integrity": "sha512-RsuMk7j6n+r752EtzyScnWkQyuJdli6LdO5Klv8Yx0OfPVTcQkIUfS8clx5e9yHXzlnhOZF3CbQ8C2uP5j074w==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.19.0", + "@babel/helper-skip-transparent-expression-wrappers": "^7.18.9" + } + }, + "@babel/plugin-transform-sticky-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.18.6.tgz", + "integrity": "sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/plugin-transform-template-literals": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.18.9.tgz", + "integrity": "sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-typeof-symbol": { + "version": "7.18.9", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.18.9.tgz", + "integrity": "sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-unicode-escapes": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.18.10.tgz", + "integrity": "sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.18.9" + } + }, + "@babel/plugin-transform-unicode-regex": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.18.6.tgz", + "integrity": "sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==", + "dev": true, + "requires": { + "@babel/helper-create-regexp-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + } + }, + "@babel/preset-env": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/preset-env/-/preset-env-7.18.10.tgz", + "integrity": "sha512-wVxs1yjFdW3Z/XkNfXKoblxoHgbtUF7/l3PvvP4m02Qz9TZ6uZGxRVYjSQeR87oQmHco9zWitW5J82DJ7sCjvA==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.18.8", + "@babel/helper-compilation-targets": "^7.18.9", + "@babel/helper-plugin-utils": "^7.18.9", + "@babel/helper-validator-option": "^7.18.6", + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "^7.18.6", + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-async-generator-functions": "^7.18.10", + "@babel/plugin-proposal-class-properties": "^7.18.6", + "@babel/plugin-proposal-class-static-block": "^7.18.6", + "@babel/plugin-proposal-dynamic-import": "^7.18.6", + "@babel/plugin-proposal-export-namespace-from": "^7.18.9", + "@babel/plugin-proposal-json-strings": "^7.18.6", + "@babel/plugin-proposal-logical-assignment-operators": "^7.18.9", + "@babel/plugin-proposal-nullish-coalescing-operator": "^7.18.6", + "@babel/plugin-proposal-numeric-separator": "^7.18.6", + "@babel/plugin-proposal-object-rest-spread": "^7.18.9", + "@babel/plugin-proposal-optional-catch-binding": "^7.18.6", + "@babel/plugin-proposal-optional-chaining": "^7.18.9", + "@babel/plugin-proposal-private-methods": "^7.18.6", + "@babel/plugin-proposal-private-property-in-object": "^7.18.6", + "@babel/plugin-proposal-unicode-property-regex": "^7.18.6", + "@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-import-assertions": "^7.18.6", + "@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.18.6", + "@babel/plugin-transform-async-to-generator": "^7.18.6", + "@babel/plugin-transform-block-scoped-functions": "^7.18.6", + "@babel/plugin-transform-block-scoping": "^7.18.9", + "@babel/plugin-transform-classes": "^7.18.9", + "@babel/plugin-transform-computed-properties": "^7.18.9", + "@babel/plugin-transform-destructuring": "^7.18.9", + "@babel/plugin-transform-dotall-regex": "^7.18.6", + "@babel/plugin-transform-duplicate-keys": "^7.18.9", + "@babel/plugin-transform-exponentiation-operator": "^7.18.6", + "@babel/plugin-transform-for-of": "^7.18.8", + "@babel/plugin-transform-function-name": "^7.18.9", + "@babel/plugin-transform-literals": "^7.18.9", + "@babel/plugin-transform-member-expression-literals": "^7.18.6", + "@babel/plugin-transform-modules-amd": "^7.18.6", + "@babel/plugin-transform-modules-commonjs": "^7.18.6", + "@babel/plugin-transform-modules-systemjs": "^7.18.9", + "@babel/plugin-transform-modules-umd": "^7.18.6", + "@babel/plugin-transform-named-capturing-groups-regex": "^7.18.6", + "@babel/plugin-transform-new-target": "^7.18.6", + "@babel/plugin-transform-object-super": "^7.18.6", + "@babel/plugin-transform-parameters": "^7.18.8", + "@babel/plugin-transform-property-literals": "^7.18.6", + "@babel/plugin-transform-regenerator": "^7.18.6", + "@babel/plugin-transform-reserved-words": "^7.18.6", + "@babel/plugin-transform-shorthand-properties": "^7.18.6", + "@babel/plugin-transform-spread": "^7.18.9", + "@babel/plugin-transform-sticky-regex": "^7.18.6", + "@babel/plugin-transform-template-literals": "^7.18.9", + "@babel/plugin-transform-typeof-symbol": "^7.18.9", + "@babel/plugin-transform-unicode-escapes": "^7.18.10", + "@babel/plugin-transform-unicode-regex": "^7.18.6", + "@babel/preset-modules": "^0.1.5", + "@babel/types": "^7.18.10", + "babel-plugin-polyfill-corejs2": "^0.3.2", + "babel-plugin-polyfill-corejs3": "^0.5.3", + "babel-plugin-polyfill-regenerator": "^0.4.0", + "core-js-compat": "^3.22.1", + "semver": "^6.3.0" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "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.18.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.18.9.tgz", + "integrity": "sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@babel/template": { + "version": "7.18.10", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", + "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/parser": "^7.18.10", + "@babel/types": "^7.18.10" + } + }, + "@babel/traverse": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.19.0.tgz", + "integrity": "sha512-4pKpFRDh+utd2mbRC8JLnlsMUii3PMHjpL6a0SZ4NMZy7YFP9aXORxEhdMVOc9CpWtDF09IkciQLEhK7Ml7gRA==", + "requires": { + "@babel/code-frame": "^7.18.6", + "@babel/generator": "^7.19.0", + "@babel/helper-environment-visitor": "^7.18.9", + "@babel/helper-function-name": "^7.19.0", + "@babel/helper-hoist-variables": "^7.18.6", + "@babel/helper-split-export-declaration": "^7.18.6", + "@babel/parser": "^7.19.0", + "@babel/types": "^7.19.0", + "debug": "^4.1.0", + "globals": "^11.1.0" + }, + "dependencies": { + "@babel/generator": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.19.0.tgz", + "integrity": "sha512-S1ahxf1gZ2dpoiFgA+ohK9DIpz50bJ0CWs7Zlzb54Z4sG8qmdIrGrVqmy1sAtTVRb+9CU6U8VqT9L0Zj7hxHVg==", + "requires": { + "@babel/types": "^7.19.0", + "@jridgewell/gen-mapping": "^0.3.2", + "jsesc": "^2.5.1" + } + }, + "@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + } + } + }, + "@babel/types": { + "version": "7.19.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.19.0.tgz", + "integrity": "sha512-YuGopBq3ke25BVSiS6fgF49Ul9gH1x70Bcr6bqRLjWCkcX8Hre1/5+z+IiWOIerRMSSEfGZVB9z9kyq7wVs9YA==", + "requires": { + "@babel/helper-string-parser": "^7.18.10", + "@babel/helper-validator-identifier": "^7.18.6", + "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 + }, + "@chevrotain/cst-dts-gen": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@chevrotain/cst-dts-gen/-/cst-dts-gen-10.3.0.tgz", + "integrity": "sha512-7DJPfCtfK1SU1/F3ZmmVv5IsTTcP/iiKFqU1m0H4VzJNvG3/GNxJFQjy4t/veWTAFSPWSj1WCNyq+sc5XKq9yA==", + "dev": true, + "requires": { + "@chevrotain/gast": "10.3.0", + "@chevrotain/types": "10.3.0", + "lodash": "4.17.21" + } + }, + "@chevrotain/gast": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@chevrotain/gast/-/gast-10.3.0.tgz", + "integrity": "sha512-kLbGubyLprlyFZ1/c5pkpciCi6WTcRcnKhkfflSdKsZuoy0OmVTEXzrmFCQWzJ+QtmQNtPZTKwIBXJ6Zixp6nA==", + "dev": true, + "requires": { + "@chevrotain/types": "10.3.0", + "lodash": "4.17.21" + } + }, + "@chevrotain/types": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@chevrotain/types/-/types-10.3.0.tgz", + "integrity": "sha512-LGesL4c5+FyweDsmFukcxmsowpagj1iC4iqkQSIDG3Y7krV2rIOmCDDq4kZff51Asr6yQHEJsWTyvGVHeWQLkw==", + "dev": true + }, + "@chevrotain/utils": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@chevrotain/utils/-/utils-10.3.0.tgz", + "integrity": "sha512-KCpFcX+kNyKlVZQW60ZUGRW5Nsg5u0F2CIgHiDQyg282ouHS9xap2ZEKqhwGE/0nYP46nAPnGPdb/IYh7ZHOsA==", + "dev": true + }, + "@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true + }, + "@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "dependencies": { + "@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + } + } + }, + "@csstools/postcss-cascade-layers": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-1.0.6.tgz", + "integrity": "sha512-ei4Vh4AJwTCXTNj7uzwduoZDO7nLPksQ0TI7OzUlyFq4P4Uhu6hU7R4AlLimDP/s6D3PQdHmRL4f7UOy370UHA==", + "dev": true, + "requires": { + "@csstools/selector-specificity": "^2.0.2", + "postcss-selector-parser": "^6.0.10" + } + }, + "@csstools/postcss-color-function": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-color-function/-/postcss-color-function-1.1.1.tgz", + "integrity": "sha512-Bc0f62WmHdtRDjf5f3e2STwRAl89N2CLb+9iAwzrv4L2hncrbDwnQD9PCq0gtAt7pOI2leIV08HIBUd4jxD8cw==", + "dev": true, + "requires": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-font-format-keywords": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-font-format-keywords/-/postcss-font-format-keywords-1.0.1.tgz", + "integrity": "sha512-ZgrlzuUAjXIOc2JueK0X5sZDjCtgimVp/O5CEqTcs5ShWBa6smhWYbS0x5cVc/+rycTDbjjzoP0KTDnUneZGOg==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-hwb-function": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-hwb-function/-/postcss-hwb-function-1.0.2.tgz", + "integrity": "sha512-YHdEru4o3Rsbjmu6vHy4UKOXZD+Rn2zmkAmLRfPet6+Jz4Ojw8cbWxe1n42VaXQhD3CQUXXTooIy8OkVbUcL+w==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-ic-unit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-ic-unit/-/postcss-ic-unit-1.0.1.tgz", + "integrity": "sha512-Ot1rcwRAaRHNKC9tAqoqNZhjdYBzKk1POgWfhN4uCOE47ebGcLRqXjKkApVDpjifL6u2/55ekkpnFcp+s/OZUw==", + "dev": true, + "requires": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-is-pseudo-class": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@csstools/postcss-is-pseudo-class/-/postcss-is-pseudo-class-2.0.7.tgz", + "integrity": "sha512-7JPeVVZHd+jxYdULl87lvjgvWldYu+Bc62s9vD/ED6/QTGjy0jy0US/f6BG53sVMTBJ1lzKZFpYmofBN9eaRiA==", + "dev": true, + "requires": { + "@csstools/selector-specificity": "^2.0.0", + "postcss-selector-parser": "^6.0.10" + } + }, + "@csstools/postcss-nested-calc": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-nested-calc/-/postcss-nested-calc-1.0.0.tgz", + "integrity": "sha512-JCsQsw1wjYwv1bJmgjKSoZNvf7R6+wuHDAbi5f/7MbFhl2d/+v+TvBTU4BJH3G1X1H87dHl0mh6TfYogbT/dJQ==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-normalize-display-values": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-normalize-display-values/-/postcss-normalize-display-values-1.0.1.tgz", + "integrity": "sha512-jcOanIbv55OFKQ3sYeFD/T0Ti7AMXc9nM1hZWu8m/2722gOTxFg7xYu4RDLJLeZmPUVQlGzo4jhzvTUq3x4ZUw==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-oklab-function": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-oklab-function/-/postcss-oklab-function-1.1.1.tgz", + "integrity": "sha512-nJpJgsdA3dA9y5pgyb/UfEzE7W5Ka7u0CX0/HIMVBNWzWemdcTH3XwANECU6anWv/ao4vVNLTMxhiPNZsTK6iA==", + "dev": true, + "requires": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-progressive-custom-properties": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-progressive-custom-properties/-/postcss-progressive-custom-properties-1.3.0.tgz", + "integrity": "sha512-ASA9W1aIy5ygskZYuWams4BzafD12ULvSypmaLJT2jvQ8G0M3I8PRQhC0h7mG0Z3LI05+agZjqSR9+K9yaQQjA==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-stepped-value-functions": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@csstools/postcss-stepped-value-functions/-/postcss-stepped-value-functions-1.0.1.tgz", + "integrity": "sha512-dz0LNoo3ijpTOQqEJLY8nyaapl6umbmDcgj4AD0lgVQ572b2eqA1iGZYTTWhrcrHztWDDRAX2DGYyw2VBjvCvQ==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-text-decoration-shorthand": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@csstools/postcss-text-decoration-shorthand/-/postcss-text-decoration-shorthand-1.0.0.tgz", + "integrity": "sha512-c1XwKJ2eMIWrzQenN0XbcfzckOLLJiczqy+YvfGmzoVXd7pT9FfObiSEfzs84bpE/VqfpEuAZ9tCRbZkZxxbdw==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-trigonometric-functions": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-trigonometric-functions/-/postcss-trigonometric-functions-1.0.2.tgz", + "integrity": "sha512-woKaLO///4bb+zZC2s80l+7cm07M7268MsyG3M0ActXXEFi6SuhvriQYcb58iiKGbjwwIU7n45iRLEHypB47Og==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "@csstools/postcss-unset-value": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-1.0.2.tgz", + "integrity": "sha512-c8J4roPBILnelAsdLr4XOAR/GsTm0GJi4XpcfvoWk3U6KiTCqiFYc63KhRMQQX35jYMp4Ao8Ij9+IZRgMfJp1g==", + "dev": true, + "requires": {} + }, + "@csstools/selector-specificity": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.0.2.tgz", + "integrity": "sha512-IkpVW/ehM1hWKln4fCA3NzJU8KwD+kIOvPZA4cqxoJHtE21CCzjyp+Kxbu0i5I4tBNOlXPL9mjwnWlL0VEG4Fg==", + "dev": true, + "requires": {} + }, + "@cypress/request": { + "version": "2.88.10", + "resolved": "https://registry.npmjs.org/@cypress/request/-/request-2.88.10.tgz", + "integrity": "sha512-Zp7F+R93N0yZyG34GutyTNr+okam7s/Fzc1+i3kcqOP8vk6OuajuE9qZJ6Rs+10/1JFtXFYMdyarnU1rZuJesg==", + "dev": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "http-signature": "~1.3.6", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^8.3.2" + }, + "dependencies": { + "qs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", + "dev": true + } + } + }, + "@cypress/xvfb": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@cypress/xvfb/-/xvfb-1.2.4.tgz", + "integrity": "sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q==", + "dev": true, + "requires": { + "debug": "^3.1.0", + "lodash.once": "^4.1.1" + }, + "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" + } + } + } + }, + "@dabh/diagnostics": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", + "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", + "dev": true, + "requires": { + "colorspace": "1.1.x", + "enabled": "2.0.x", + "kuler": "^2.0.0" + } + }, + "@discoveryjs/json-ext": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz", + "integrity": "sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==", + "dev": true + }, + "@esbuild/linux-loong64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.15.5.tgz", + "integrity": "sha512-UHkDFCfSGTuXq08oQltXxSZmH1TXyWsL+4QhZDWvvLl6mEJQqk3u7/wq1LjhrrAXYIllaTtRSzUXl4Olkf2J8A==", + "dev": true, + "optional": true + }, + "@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^3.3.0" + } + }, + "@eslint-community/regexpp": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.5.1.tgz", + "integrity": "sha512-Z5ba73P98O1KUYCCJTUeVpja9RcGoMdncZ6T49FCUl2lN38JtCJ+3WgIDBv0AuY4WChU5PmtJmOCTlN6FZTFKQ==", + "dev": true + }, + "@eslint/eslintrc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.0.3.tgz", + "integrity": "sha512-+5gy6OQfk+xx3q0d6jGZZC3f3KzAkXc/IanVxd1is/VIIziRqqt3ongQz0FiTUXqTk0c7aDB3OaFuKnuSoJicQ==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.5.2", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "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" + } + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "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" + } + }, + "globals": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "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 + }, + "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" + } + }, + "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/js": { + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.42.0.tgz", + "integrity": "sha512-6SWlXpWU5AvId8Ac7zjzmIOqMOba/JWY8XZ4A7q7Gn1Vlfg/SFFIlrtHXt9nPn4op9ZPAkl91Jao+QQv3r/ukw==", + "dev": true + }, + "@faker-js/faker": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-5.5.3.tgz", + "integrity": "sha512-R11tGE6yIFwqpaIqcfkcg7AICXzFg14+5h5v0TfF/9+RMDL6jhzCy/pxHVOfbALGdtVYdt6JdR21tuxEgl34dw==", + "dev": true + }, + "@fortawesome/angular-fontawesome": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/@fortawesome/angular-fontawesome/-/angular-fontawesome-0.11.1.tgz", + "integrity": "sha512-Ngzm5MVxk76ZhYpPTNOI/mpYNz9bzwfBXC5L9mktLgOONjBuYBPVt+bH8lny8hNtDk0ppZzXsMN6CO7hckdfnw==", + "requires": { + "tslib": "^2.4.0" + } + }, + "@fortawesome/fontawesome-common-types": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.2.0.tgz", + "integrity": "sha512-rBevIsj2nclStJ7AxTdfsa3ovHb1H+qApwrxcTVo+NNdeJiB9V75hsKfrkG5AwNcRUNxrPPiScGYCNmLMoh8pg==" + }, + "@fortawesome/fontawesome-svg-core": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.4.0.tgz", + "integrity": "sha512-Bertv8xOiVELz5raB2FlXDPKt+m94MQ3JgDfsVbrqNpLU9+UE2E18GKjLKw+d3XbeYPqg1pzyQKGsrzbw+pPaw==", + "requires": { + "@fortawesome/fontawesome-common-types": "6.4.0" + }, + "dependencies": { + "@fortawesome/fontawesome-common-types": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.4.0.tgz", + "integrity": "sha512-HNii132xfomg5QVZw0HwXXpN22s7VBHQBv9CeOu9tfJnhsWQNd2lmTNi8CSrnw5B+5YOmzu1UoPAyxaXsJ6RgQ==" + } + } + }, + "@fortawesome/free-solid-svg-icons": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/@fortawesome/free-solid-svg-icons/-/free-solid-svg-icons-6.2.0.tgz", + "integrity": "sha512-UjCILHIQ4I8cN46EiQn0CZL/h8AwCGgR//1c4R96Q5viSRwuKVo0NdQEc4bm+69ZwC0dUvjbDqAHF1RR5FA3XA==", + "requires": { + "@fortawesome/fontawesome-common-types": "6.2.0" + } + }, + "@gar/promisify": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", + "dev": true + }, + "@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==", + "dev": true + }, + "@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", + "dev": true, + "requires": { + "@hapi/hoek": "^9.0.0" + } + }, + "@humanwhocodes/config-array": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz", + "integrity": "sha512-KVVjQmNUepDVGXNuoRRdmmEjruj0KfiGSbS8LVc12LMsWDQzRXJ0qdhN8L8uUigKpfEHRhlaQFY0ib1tnUbNeQ==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "dependencies": { + "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" + } + }, + "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" + } + } + } + }, + "@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true + }, + "@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 + }, + "@isaacs/string-locale-compare": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@isaacs/string-locale-compare/-/string-locale-compare-1.1.0.tgz", + "integrity": "sha512-SQ7Kzhh9+D+ZW9MA0zkYv3VXhIDNx+LzM6EJ+/65I3QY+enU6Itte7E5XX7EWrqLW2FN4n06GWzBnPoC3th2aQ==", + "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" + } + }, + "@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": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-28.1.3.tgz", + "integrity": "sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw==", + "dev": true, + "requires": { + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^28.1.3", + "jest-util": "^28.1.3", + "slash": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.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==", + "dev": true, + "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==", + "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==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@jest/core": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-28.1.3.tgz", + "integrity": "sha512-CIKBrlaKOzA7YG19BEqCw3SLIsEwjZkeJzf5bdooVnW4bH5cktqe3JX+G2YV1aK5vP8N9na1IGWFzYaTp6k6NA==", + "dev": true, + "requires": { + "@jest/console": "^28.1.3", + "@jest/reporters": "^28.1.3", + "@jest/test-result": "^28.1.3", + "@jest/transform": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^28.1.3", + "jest-config": "^28.1.3", + "jest-haste-map": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-regex-util": "^28.0.2", + "jest-resolve": "^28.1.3", + "jest-resolve-dependencies": "^28.1.3", + "jest-runner": "^28.1.3", + "jest-runtime": "^28.1.3", + "jest-snapshot": "^28.1.3", + "jest-util": "^28.1.3", + "jest-validate": "^28.1.3", + "jest-watcher": "^28.1.3", + "micromatch": "^4.0.4", + "pretty-format": "^28.1.3", + "rimraf": "^3.0.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.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==", + "dev": true, + "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==", + "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==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@jest/environment": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-28.1.3.tgz", + "integrity": "sha512-1bf40cMFTEkKyEf585R9Iz1WayDjHoHqvts0XFYEqyKM3cFWDpeMoqKKTAF9LSYQModPUlh8FKptoM2YcMWAXA==", + "dev": true, + "requires": { + "@jest/fake-timers": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "jest-mock": "^28.1.3" + } + }, + "@jest/expect": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-28.1.3.tgz", + "integrity": "sha512-lzc8CpUbSoE4dqT0U+g1qODQjBRHPpCPXissXD4mS9+sWQdmmpeJ9zSH1rS1HEkrsMN0fb7nKrJ9giAR1d3wBw==", + "dev": true, + "requires": { + "expect": "^28.1.3", + "jest-snapshot": "^28.1.3" + } + }, + "@jest/expect-utils": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-28.1.3.tgz", + "integrity": "sha512-wvbi9LUrHJLn3NlDW6wF2hvIMtd4JUl2QNVrjq+IBSHirgfrR3o9RnVtxzdEGO2n9JyIWwHnLfby5KzqBGg2YA==", + "dev": true, + "requires": { + "jest-get-type": "^28.0.2" + } + }, + "@jest/fake-timers": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-28.1.3.tgz", + "integrity": "sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw==", + "dev": true, + "requires": { + "@jest/types": "^28.1.3", + "@sinonjs/fake-timers": "^9.1.2", + "@types/node": "*", + "jest-message-util": "^28.1.3", + "jest-mock": "^28.1.3", + "jest-util": "^28.1.3" + } + }, + "@jest/globals": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-28.1.3.tgz", + "integrity": "sha512-XFU4P4phyryCXu1pbcqMO0GSQcYe1IsalYCDzRNyhetyeyxMcIxa11qPNDpVNLeretItNqEmYYQn1UYz/5x1NA==", + "dev": true, + "requires": { + "@jest/environment": "^28.1.3", + "@jest/expect": "^28.1.3", + "@jest/types": "^28.1.3" + } + }, + "@jest/reporters": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-28.1.3.tgz", + "integrity": "sha512-JuAy7wkxQZVNU/V6g9xKzCGC5LVXx9FDcABKsSXp5MiKPEE2144a/vXTEDoyzjUpZKfVwp08Wqg5A4WfTMAzjg==", + "dev": true, + "requires": { + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^28.1.3", + "@jest/test-result": "^28.1.3", + "@jest/transform": "^28.1.3", + "@jest/types": "^28.1.3", + "@jridgewell/trace-mapping": "^0.3.13", + "@types/node": "*", + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "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-message-util": "^28.1.3", + "jest-util": "^28.1.3", + "jest-worker": "^28.1.3", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "terminal-link": "^2.0.0", + "v8-to-istanbul": "^9.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.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" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.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==", + "dev": true, + "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==", + "dev": true + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "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" + } + }, + "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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@jest/schemas": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", + "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", + "dev": true, + "requires": { + "@sinclair/typebox": "^0.24.1" + } + }, + "@jest/source-map": { + "version": "28.1.2", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-28.1.2.tgz", + "integrity": "sha512-cV8Lx3BeStJb8ipPHnqVw/IM2VCMWO3crWZzYodSIkxXnRcXJipCdx1JCK0K5MsJJouZQTH73mzf4vgxRaH9ww==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.13", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + } + }, + "@jest/test-result": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-28.1.3.tgz", + "integrity": "sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg==", + "dev": true, + "requires": { + "@jest/console": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" + } + }, + "@jest/test-sequencer": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-28.1.3.tgz", + "integrity": "sha512-NIMPEqqa59MWnDi1kvXXpYbqsfQmSJsIbnd85mdVGkiDfQ9WQQTXOLsvISUfonmnBT+w85WEgneCigEEdHDFxw==", + "dev": true, + "requires": { + "@jest/test-result": "^28.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^28.1.3", + "slash": "^3.0.0" + } + }, + "@jest/transform": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-28.1.3.tgz", + "integrity": "sha512-u5dT5di+oFI6hfcLOHGTAfmUxFRrjK+vnaP0kkVow9Md/M7V/MxqQMOz/VV25UZO8pzeA9PjfTpOu6BDuwSPQA==", + "dev": true, + "requires": { + "@babel/core": "^7.11.6", + "@jest/types": "^28.1.3", + "@jridgewell/trace-mapping": "^0.3.13", + "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": "^28.1.3", + "jest-regex-util": "^28.0.2", + "jest-util": "^28.1.3", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.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==", + "dev": true, + "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==", + "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==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@jest/types": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", + "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", + "dev": true, + "requires": { + "@jest/schemas": "^28.1.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.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==", + "dev": true, + "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==", + "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==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "@jridgewell/gen-mapping": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", + "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "requires": { + "@jridgewell/set-array": "^1.0.0", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@jridgewell/resolve-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz", + "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==" + }, + "@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==" + }, + "@jridgewell/source-map": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.2.tgz", + "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==", + "dev": true, + "requires": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "dependencies": { + "@jridgewell/gen-mapping": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", + "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "dev": true, + "requires": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + } + } + } + }, + "@jridgewell/sourcemap-codec": { + "version": "1.4.14", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", + "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==" + }, + "@jridgewell/trace-mapping": { + "version": "0.3.18", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz", + "integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==", + "requires": { + "@jridgewell/resolve-uri": "3.1.0", + "@jridgewell/sourcemap-codec": "1.4.14" + } + }, + "@kwsites/file-exists": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", + "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==", + "dev": true, + "requires": { + "debug": "^4.1.1" + } + }, + "@kwsites/promise-deferred": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz", + "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==", + "dev": true + }, + "@leichtgewicht/ip-codec": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", + "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==", + "dev": true + }, + "@mattlewis92/dom-autoscroller": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@mattlewis92/dom-autoscroller/-/dom-autoscroller-2.4.2.tgz", + "integrity": "sha512-YbrUWREPGEjE/FU6foXcAT1YbVwqD/jkYnY1dFb0o4AxtP3s4xKBthlELjndZih8uwsDWgQZx1eNskRNe2BgZQ==" + }, + "@ng-bootstrap/ng-bootstrap": { + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/@ng-bootstrap/ng-bootstrap/-/ng-bootstrap-13.1.1.tgz", + "integrity": "sha512-R6qnmFKT2EwwijBHw7rUXqyo5W90OImHOv7BlsxMNnZLIksWIhqwU00k4UBTfRTnd6JsTPuj/co3MaP61ajILA==", + "requires": { + "tslib": "^2.3.0" + } + }, + "@ngtools/webpack": { + "version": "14.2.1", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-14.2.1.tgz", + "integrity": "sha512-9tsfx2ZQscnpszVkdoJIIxZZDwitgmbPNrsQmyfHhwqPeJ8UUnAk6RNTfHjtX31VCmIM+qdGJsX7vDR9ye3/uA==", + "dev": true, + "requires": {} + }, + "@ngx-translate/core": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@ngx-translate/core/-/core-14.0.0.tgz", + "integrity": "sha512-UevdwNCXMRCdJv//0kC8h2eSfmi02r29xeE8E9gJ1Al4D4jEJ7eiLPdjslTMc21oJNGguqqWeEVjf64SFtvw2w==", + "requires": { + "tslib": "^2.3.0" + } + }, + "@ngx-translate/http-loader": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@ngx-translate/http-loader/-/http-loader-7.0.0.tgz", + "integrity": "sha512-j+NpXXlcGVdyUNyY/qsJrqqeAdJdizCd+GKh3usXExSqy1aE9866jlAIL+xrfDU4w+LiMoma5pgE4emvFebZmA==", + "requires": { + "tslib": "^2.3.0" + } + }, + "@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/arborist": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@npmcli/arborist/-/arborist-4.3.1.tgz", + "integrity": "sha512-yMRgZVDpwWjplorzt9SFSaakWx6QIK248Nw4ZFgkrAy/GvJaFRaSZzE6nD7JBK5r8g/+PTxFq5Wj/sfciE7x+A==", + "dev": true, + "requires": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/installed-package-contents": "^1.0.7", + "@npmcli/map-workspaces": "^2.0.0", + "@npmcli/metavuln-calculator": "^2.0.0", + "@npmcli/move-file": "^1.1.0", + "@npmcli/name-from-folder": "^1.0.1", + "@npmcli/node-gyp": "^1.0.3", + "@npmcli/package-json": "^1.0.1", + "@npmcli/run-script": "^2.0.0", + "bin-links": "^3.0.0", + "cacache": "^15.0.3", + "common-ancestor-path": "^1.0.1", + "json-parse-even-better-errors": "^2.3.1", + "json-stringify-nice": "^1.1.4", + "mkdirp": "^1.0.4", + "mkdirp-infer-owner": "^2.0.0", + "npm-install-checks": "^4.0.0", + "npm-package-arg": "^8.1.5", + "npm-pick-manifest": "^6.1.0", + "npm-registry-fetch": "^12.0.1", + "pacote": "^12.0.2", + "parse-conflict-json": "^2.0.1", + "proc-log": "^1.0.0", + "promise-all-reject-late": "^1.0.0", + "promise-call-limit": "^1.0.1", + "read-package-json-fast": "^2.0.2", + "readdir-scoped-modules": "^1.1.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "ssri": "^8.0.1", + "treeverse": "^1.0.4", + "walk-up-path": "^1.0.0" + }, + "dependencies": { + "@npmcli/fs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", + "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", + "dev": true, + "requires": { + "@gar/promisify": "^1.0.1", + "semver": "^7.3.5" + } + }, + "@npmcli/git": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-2.1.0.tgz", + "integrity": "sha512-/hBFX/QG1b+N7PZBFs0bi+evgRZcK9nWBxQKZkGoXUT5hJSwl5c4d7y8/hm+NQZRPhQ67RzFaj5UM9YeyKoryw==", + "dev": true, + "requires": { + "@npmcli/promise-spawn": "^1.3.2", + "lru-cache": "^6.0.0", + "mkdirp": "^1.0.4", + "npm-pick-manifest": "^6.1.1", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^2.0.2" + } + }, + "@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" + } + }, + "@npmcli/node-gyp": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-1.0.3.tgz", + "integrity": "sha512-fnkhw+fmX65kiLqk6E3BFLXNC26rUhK90zVwe2yncPliVT/Qos3xjhTLE59Df8KnPlcwIERXKVlU1bXoUQ+liA==", + "dev": true + }, + "@npmcli/promise-spawn": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-1.3.2.tgz", + "integrity": "sha512-QyAGYo/Fbj4MXeGdJcFzZ+FkDkomfRBrPM+9QYJSg+PxgAUL+LU3FneQk37rKR2/zjqkCV1BLHccX98wRXG3Sg==", + "dev": true, + "requires": { + "infer-owner": "^1.0.4" + } + }, + "@npmcli/run-script": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-2.0.0.tgz", + "integrity": "sha512-fSan/Pu11xS/TdaTpTB0MRn9guwGU8dye+x56mEVgBEd/QsybBbYcAL0phPXi8SGWFEChkQd6M9qL4y6VOpFig==", + "dev": true, + "requires": { + "@npmcli/node-gyp": "^1.0.2", + "@npmcli/promise-spawn": "^1.3.2", + "node-gyp": "^8.2.0", + "read-package-json-fast": "^2.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 + }, + "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" + } + }, + "builtins": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz", + "integrity": "sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==", + "dev": true + }, + "cacache": { + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", + "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", + "dev": true, + "requires": { + "@npmcli/fs": "^1.0.0", + "@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" + } + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "requires": { + "lru-cache": "^6.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" + } + }, + "ignore-walk": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-4.0.1.tgz", + "integrity": "sha512-rzDQLaW4jQbh2YrOFlJdCtX8qgJTehFRYiUB2r1osqTeDzV/3+Jh8fz1oAPzUThf3iku8Ds4IDqawI5d8mUiQw==", + "dev": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "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==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "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" + } + }, + "minipass-fetch": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", + "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", + "dev": true, + "requires": { + "encoding": "^0.1.12", + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" + } + }, + "node-gyp": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", + "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", + "dev": true, + "requires": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^9.1.0", + "nopt": "^5.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "dependencies": { + "make-fetch-happen": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", + "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", + "dev": true, + "requires": { + "agentkeepalive": "^4.1.3", + "cacache": "^15.2.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.2", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^6.0.0", + "ssri": "^8.0.0" + } + } + } + }, + "npm-install-checks": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-4.0.0.tgz", + "integrity": "sha512-09OmyDkNLYwqKPOnbI8exiOZU2GVVmQp7tgez2BPi5OZC8M82elDAps7sxC4l//uSUtotWqoEIDwjRvWH4qz8w==", + "dev": true, + "requires": { + "semver": "^7.1.1" + } + }, + "npm-package-arg": { + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-8.1.5.tgz", + "integrity": "sha512-LhgZrg0n0VgvzVdSm1oiZworPbTxYHUJCgtsJW8mGvlDpxTM1vSJc3m5QZeUkhAHIzbz3VCHd/R4osi1L1Tg/Q==", + "dev": true, + "requires": { + "hosted-git-info": "^4.0.1", + "semver": "^7.3.4", + "validate-npm-package-name": "^3.0.0" + } + }, + "npm-packlist": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-3.0.0.tgz", + "integrity": "sha512-L/cbzmutAwII5glUcf2DBRNY/d0TFd4e/FnaZigJV6JD85RHZXJFGwCndjMWiiViiWSsWt3tiOLpI3ByTnIdFQ==", + "dev": true, + "requires": { + "glob": "^7.1.6", + "ignore-walk": "^4.0.1", + "npm-bundled": "^1.1.1", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-pick-manifest": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-6.1.1.tgz", + "integrity": "sha512-dBsdBtORT84S8V8UTad1WlUyKIY9iMsAmqxHbLdeEeBNMLQDlDWWra3wYUx9EBEIiG/YwAy0XyNHDd2goAsfuA==", + "dev": true, + "requires": { + "npm-install-checks": "^4.0.0", + "npm-normalize-package-bin": "^1.0.1", + "npm-package-arg": "^8.1.2", + "semver": "^7.3.4" + } + }, + "npm-registry-fetch": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-12.0.2.tgz", + "integrity": "sha512-Df5QT3RaJnXYuOwtXBXS9BWs+tHH2olvkCLh6jcR/b/u3DvPMlp3J0TvvYwplPKxHMOwfg287PYih9QqaVFoKA==", + "dev": true, + "requires": { + "make-fetch-happen": "^10.0.1", + "minipass": "^3.1.6", + "minipass-fetch": "^1.4.1", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.1.2", + "npm-package-arg": "^8.1.5" + } + }, + "pacote": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-12.0.3.tgz", + "integrity": "sha512-CdYEl03JDrRO3x18uHjBYA9TyoW8gy+ThVcypcDkxPtKlw76e4ejhYB6i9lJ+/cebbjpqPW/CijjqxwDTts8Ow==", + "dev": true, + "requires": { + "@npmcli/git": "^2.1.0", + "@npmcli/installed-package-contents": "^1.0.6", + "@npmcli/promise-spawn": "^1.2.0", + "@npmcli/run-script": "^2.0.0", + "cacache": "^15.0.5", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "infer-owner": "^1.0.4", + "minipass": "^3.1.3", + "mkdirp": "^1.0.3", + "npm-package-arg": "^8.0.1", + "npm-packlist": "^3.0.0", + "npm-pick-manifest": "^6.0.0", + "npm-registry-fetch": "^12.0.0", + "promise-retry": "^2.0.1", + "read-package-json-fast": "^2.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.1.0" + } + }, + "proc-log": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-1.0.0.tgz", + "integrity": "sha512-aCk8AO51s+4JyuYGg3Q/a6gnrlDO09NpVWePtjp7xwphcoQ04x5WAfCyugcsbLooWcMJ87CLkD4+604IckEdhg==", + "dev": true + }, + "socks-proxy-agent": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz", + "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==", + "dev": true, + "requires": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + } + }, + "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" + } + }, + "validate-npm-package-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz", + "integrity": "sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw==", + "dev": true, + "requires": { + "builtins": "^1.0.3" + } + } + } + }, + "@npmcli/fs": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.2.tgz", + "integrity": "sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==", + "dev": true, + "requires": { + "@gar/promisify": "^1.1.3", + "semver": "^7.3.5" + } + }, + "@npmcli/git": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-3.0.2.tgz", + "integrity": "sha512-CAcd08y3DWBJqJDpfuVL0uijlq5oaXaOJEKHKc4wqrjd00gkvTZB+nFuLn+doOOKddaQS9JfqtNoFCO2LCvA3w==", + "dev": true, + "requires": { + "@npmcli/promise-spawn": "^3.0.0", + "lru-cache": "^7.4.4", + "mkdirp": "^1.0.4", + "npm-pick-manifest": "^7.0.0", + "proc-log": "^2.0.0", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^2.0.2" + } + }, + "@npmcli/installed-package-contents": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@npmcli/installed-package-contents/-/installed-package-contents-1.0.7.tgz", + "integrity": "sha512-9rufe0wnJusCQoLpV9ZPKIVP55itrM5BxOXs10DmdbRfgWtHy1LDyskbwRnBghuB0PrF7pNPOqREVtpz4HqzKw==", + "dev": true, + "requires": { + "npm-bundled": "^1.1.1", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "@npmcli/map-workspaces": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@npmcli/map-workspaces/-/map-workspaces-2.0.4.tgz", + "integrity": "sha512-bMo0aAfwhVwqoVM5UzX1DJnlvVvzDCHae821jv48L1EsrYwfOZChlqWYXEtto/+BkBXetPbEWgau++/brh4oVg==", + "dev": true, + "requires": { + "@npmcli/name-from-folder": "^1.0.1", + "glob": "^8.0.1", + "minimatch": "^5.0.1", + "read-package-json-fast": "^2.0.3" + } + }, + "@npmcli/metavuln-calculator": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/metavuln-calculator/-/metavuln-calculator-2.0.0.tgz", + "integrity": "sha512-VVW+JhWCKRwCTE+0xvD6p3uV4WpqocNYYtzyvenqL/u1Q3Xx6fGTJ+6UoIoii07fbuEO9U3IIyuGY0CYHDv1sg==", + "dev": true, + "requires": { + "cacache": "^15.0.5", + "json-parse-even-better-errors": "^2.3.1", + "pacote": "^12.0.0", + "semver": "^7.3.2" + }, + "dependencies": { + "@npmcli/fs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", + "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", + "dev": true, + "requires": { + "@gar/promisify": "^1.0.1", + "semver": "^7.3.5" + } + }, + "@npmcli/git": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-2.1.0.tgz", + "integrity": "sha512-/hBFX/QG1b+N7PZBFs0bi+evgRZcK9nWBxQKZkGoXUT5hJSwl5c4d7y8/hm+NQZRPhQ67RzFaj5UM9YeyKoryw==", + "dev": true, + "requires": { + "@npmcli/promise-spawn": "^1.3.2", + "lru-cache": "^6.0.0", + "mkdirp": "^1.0.4", + "npm-pick-manifest": "^6.1.1", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^2.0.2" + } + }, + "@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" + } + }, + "@npmcli/node-gyp": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-1.0.3.tgz", + "integrity": "sha512-fnkhw+fmX65kiLqk6E3BFLXNC26rUhK90zVwe2yncPliVT/Qos3xjhTLE59Df8KnPlcwIERXKVlU1bXoUQ+liA==", + "dev": true + }, + "@npmcli/promise-spawn": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-1.3.2.tgz", + "integrity": "sha512-QyAGYo/Fbj4MXeGdJcFzZ+FkDkomfRBrPM+9QYJSg+PxgAUL+LU3FneQk37rKR2/zjqkCV1BLHccX98wRXG3Sg==", + "dev": true, + "requires": { + "infer-owner": "^1.0.4" + } + }, + "@npmcli/run-script": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-2.0.0.tgz", + "integrity": "sha512-fSan/Pu11xS/TdaTpTB0MRn9guwGU8dye+x56mEVgBEd/QsybBbYcAL0phPXi8SGWFEChkQd6M9qL4y6VOpFig==", + "dev": true, + "requires": { + "@npmcli/node-gyp": "^1.0.2", + "@npmcli/promise-spawn": "^1.3.2", + "node-gyp": "^8.2.0", + "read-package-json-fast": "^2.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 + }, + "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" + } + }, + "builtins": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz", + "integrity": "sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==", + "dev": true + }, + "cacache": { + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", + "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", + "dev": true, + "requires": { + "@npmcli/fs": "^1.0.0", + "@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" + } + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "requires": { + "lru-cache": "^6.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" + } + }, + "ignore-walk": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-4.0.1.tgz", + "integrity": "sha512-rzDQLaW4jQbh2YrOFlJdCtX8qgJTehFRYiUB2r1osqTeDzV/3+Jh8fz1oAPzUThf3iku8Ds4IDqawI5d8mUiQw==", + "dev": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "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==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "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" + } + }, + "minipass-fetch": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", + "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", + "dev": true, + "requires": { + "encoding": "^0.1.12", + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" + } + }, + "node-gyp": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", + "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", + "dev": true, + "requires": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^9.1.0", + "nopt": "^5.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "dependencies": { + "make-fetch-happen": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", + "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", + "dev": true, + "requires": { + "agentkeepalive": "^4.1.3", + "cacache": "^15.2.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.2", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^6.0.0", + "ssri": "^8.0.0" + } + } + } + }, + "npm-install-checks": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-4.0.0.tgz", + "integrity": "sha512-09OmyDkNLYwqKPOnbI8exiOZU2GVVmQp7tgez2BPi5OZC8M82elDAps7sxC4l//uSUtotWqoEIDwjRvWH4qz8w==", + "dev": true, + "requires": { + "semver": "^7.1.1" + } + }, + "npm-package-arg": { + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-8.1.5.tgz", + "integrity": "sha512-LhgZrg0n0VgvzVdSm1oiZworPbTxYHUJCgtsJW8mGvlDpxTM1vSJc3m5QZeUkhAHIzbz3VCHd/R4osi1L1Tg/Q==", + "dev": true, + "requires": { + "hosted-git-info": "^4.0.1", + "semver": "^7.3.4", + "validate-npm-package-name": "^3.0.0" + } + }, + "npm-packlist": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-3.0.0.tgz", + "integrity": "sha512-L/cbzmutAwII5glUcf2DBRNY/d0TFd4e/FnaZigJV6JD85RHZXJFGwCndjMWiiViiWSsWt3tiOLpI3ByTnIdFQ==", + "dev": true, + "requires": { + "glob": "^7.1.6", + "ignore-walk": "^4.0.1", + "npm-bundled": "^1.1.1", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-pick-manifest": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-6.1.1.tgz", + "integrity": "sha512-dBsdBtORT84S8V8UTad1WlUyKIY9iMsAmqxHbLdeEeBNMLQDlDWWra3wYUx9EBEIiG/YwAy0XyNHDd2goAsfuA==", + "dev": true, + "requires": { + "npm-install-checks": "^4.0.0", + "npm-normalize-package-bin": "^1.0.1", + "npm-package-arg": "^8.1.2", + "semver": "^7.3.4" + } + }, + "npm-registry-fetch": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-12.0.2.tgz", + "integrity": "sha512-Df5QT3RaJnXYuOwtXBXS9BWs+tHH2olvkCLh6jcR/b/u3DvPMlp3J0TvvYwplPKxHMOwfg287PYih9QqaVFoKA==", + "dev": true, + "requires": { + "make-fetch-happen": "^10.0.1", + "minipass": "^3.1.6", + "minipass-fetch": "^1.4.1", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.1.2", + "npm-package-arg": "^8.1.5" + } + }, + "pacote": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-12.0.3.tgz", + "integrity": "sha512-CdYEl03JDrRO3x18uHjBYA9TyoW8gy+ThVcypcDkxPtKlw76e4ejhYB6i9lJ+/cebbjpqPW/CijjqxwDTts8Ow==", + "dev": true, + "requires": { + "@npmcli/git": "^2.1.0", + "@npmcli/installed-package-contents": "^1.0.6", + "@npmcli/promise-spawn": "^1.2.0", + "@npmcli/run-script": "^2.0.0", + "cacache": "^15.0.5", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "infer-owner": "^1.0.4", + "minipass": "^3.1.3", + "mkdirp": "^1.0.3", + "npm-package-arg": "^8.0.1", + "npm-packlist": "^3.0.0", + "npm-pick-manifest": "^6.0.0", + "npm-registry-fetch": "^12.0.0", + "promise-retry": "^2.0.1", + "read-package-json-fast": "^2.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.1.0" + } + }, + "socks-proxy-agent": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz", + "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==", + "dev": true, + "requires": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + } + }, + "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" + } + }, + "validate-npm-package-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz", + "integrity": "sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw==", + "dev": true, + "requires": { + "builtins": "^1.0.3" + } + } + } + }, + "@npmcli/move-file": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-2.0.1.tgz", + "integrity": "sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==", + "dev": true, + "requires": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + } + }, + "@npmcli/name-from-folder": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/name-from-folder/-/name-from-folder-1.0.1.tgz", + "integrity": "sha512-qq3oEfcLFwNfEYOQ8HLimRGKlD8WSeGEdtUa7hmzpR8Sa7haL1KVQrvgO6wqMjhWFFVjgtrh1gIxDz+P8sjUaA==", + "dev": true + }, + "@npmcli/node-gyp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-2.0.0.tgz", + "integrity": "sha512-doNI35wIe3bBaEgrlPfdJPaCpUR89pJWep4Hq3aRdh6gKazIVWfs0jHttvSSoq47ZXgC7h73kDsUl8AoIQUB+A==", + "dev": true + }, + "@npmcli/package-json": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/package-json/-/package-json-1.0.1.tgz", + "integrity": "sha512-y6jnu76E9C23osz8gEMBayZmaZ69vFOIk8vR1FJL/wbEJ54+9aVG9rLTjQKSXfgYZEr50nw1txBBFfBZZe+bYg==", + "dev": true, + "requires": { + "json-parse-even-better-errors": "^2.3.1" + } + }, + "@npmcli/promise-spawn": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-3.0.0.tgz", + "integrity": "sha512-s9SgS+p3a9Eohe68cSI3fi+hpcZUmXq5P7w0kMlAsWVtR7XbK3ptkZqKT2cK1zLDObJ3sR+8P59sJE0w/KTL1g==", + "dev": true, + "requires": { + "infer-owner": "^1.0.4" + } + }, + "@npmcli/run-script": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-4.2.1.tgz", + "integrity": "sha512-7dqywvVudPSrRCW5nTHpHgeWnbBtz8cFkOuKrecm6ih+oO9ciydhWt6OF7HlqupRRmB8Q/gECVdB9LMfToJbRg==", + "dev": true, + "requires": { + "@npmcli/node-gyp": "^2.0.0", + "@npmcli/promise-spawn": "^3.0.0", + "node-gyp": "^9.0.0", + "read-package-json-fast": "^2.0.3", + "which": "^2.0.2" + } + }, + "@octokit/auth-token": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz", + "integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==", + "dev": true, + "requires": { + "@octokit/types": "^6.0.3" + } + }, + "@octokit/core": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz", + "integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==", + "dev": true, + "requires": { + "@octokit/auth-token": "^2.4.4", + "@octokit/graphql": "^4.5.8", + "@octokit/request": "^5.6.3", + "@octokit/request-error": "^2.0.5", + "@octokit/types": "^6.0.3", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/endpoint": { + "version": "6.0.12", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz", + "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==", + "dev": true, + "requires": { + "@octokit/types": "^6.0.3", + "is-plain-object": "^5.0.0", + "universal-user-agent": "^6.0.0" + }, + "dependencies": { + "is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true + } + } + }, + "@octokit/graphql": { + "version": "4.8.0", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz", + "integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==", + "dev": true, + "requires": { + "@octokit/request": "^5.6.0", + "@octokit/types": "^6.0.3", + "universal-user-agent": "^6.0.0" + } + }, + "@octokit/openapi-types": { + "version": "12.11.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-12.11.0.tgz", + "integrity": "sha512-VsXyi8peyRq9PqIz/tpqiL2w3w80OgVMwBHltTml3LmVvXiphgeqmY9mvBw9Wu7e0QWk/fqD37ux8yP5uVekyQ==", + "dev": true + }, + "@octokit/plugin-paginate-rest": { + "version": "2.21.3", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.21.3.tgz", + "integrity": "sha512-aCZTEf0y2h3OLbrgKkrfFdjRL6eSOo8komneVQJnYecAxIej7Bafor2xhuDJOIFau4pk0i/P28/XgtbyPF0ZHw==", + "dev": true, + "requires": { + "@octokit/types": "^6.40.0" + } + }, + "@octokit/plugin-request-log": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", + "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==", + "dev": true, + "requires": {} + }, + "@octokit/plugin-rest-endpoint-methods": { + "version": "5.16.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.16.2.tgz", + "integrity": "sha512-8QFz29Fg5jDuTPXVtey05BLm7OB+M8fnvE64RNegzX7U+5NUXcOcnpTIK0YfSHBg8gYd0oxIq3IZTe9SfPZiRw==", + "dev": true, + "requires": { + "@octokit/types": "^6.39.0", + "deprecation": "^2.3.1" + } + }, + "@octokit/request": { + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz", + "integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==", + "dev": true, + "requires": { + "@octokit/endpoint": "^6.0.1", + "@octokit/request-error": "^2.1.0", + "@octokit/types": "^6.16.1", + "is-plain-object": "^5.0.0", + "node-fetch": "^2.6.7", + "universal-user-agent": "^6.0.0" + }, + "dependencies": { + "is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "dev": true + } + } + }, + "@octokit/request-error": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", + "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", + "dev": true, + "requires": { + "@octokit/types": "^6.0.3", + "deprecation": "^2.0.0", + "once": "^1.4.0" + } + }, + "@octokit/rest": { + "version": "18.12.0", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.12.0.tgz", + "integrity": "sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q==", + "dev": true, + "requires": { + "@octokit/core": "^3.5.1", + "@octokit/plugin-paginate-rest": "^2.16.8", + "@octokit/plugin-request-log": "^1.0.4", + "@octokit/plugin-rest-endpoint-methods": "^5.12.0" + } + }, + "@octokit/types": { + "version": "6.41.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.41.0.tgz", + "integrity": "sha512-eJ2jbzjdijiL3B4PrSQaSjuF2sPEQPVCPzBvTHJD9Nz+9dw2SGH4K4xeQJ77YfTq5bRQ+bD8wT11JbeDPmxmGg==", + "dev": true, + "requires": { + "@octokit/openapi-types": "^12.11.0" + } + }, + "@pkgr/utils": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@pkgr/utils/-/utils-2.3.1.tgz", + "integrity": "sha512-wfzX8kc1PMyUILA+1Z/EqoE4UCXGy0iRGMhPwdfae1+f0OXlLqCk+By+aMzgJBzR9AzS4CDizioG6Ss1gvAFJw==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "is-glob": "^4.0.3", + "open": "^8.4.0", + "picocolors": "^1.0.0", + "tiny-glob": "^0.2.9", + "tslib": "^2.4.0" + } + }, + "@polka/url": { + "version": "1.0.0-next.21", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.21.tgz", + "integrity": "sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==", + "dev": true + }, + "@popperjs/core": { + "version": "2.11.6", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz", + "integrity": "sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw==" + }, + "@scarf/scarf": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@scarf/scarf/-/scarf-1.1.1.tgz", + "integrity": "sha512-VGbKDbk1RFIaSmdVb0cNjjWJoRWRI/Weo23AjRCC2nryO0iAS8pzsToJfPVPtVs74WHw4L1UTADNdIYRLkirZQ==" + }, + "@schematics/angular": { + "version": "14.2.1", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-14.2.1.tgz", + "integrity": "sha512-Dchixep/FMETAMuyFchw9Nryi7tfuZQRumzIOtQpv+KaVtfjvcIlES0KuI0U3Qh7tGIYPBmO3Mkt3oojcl2RBA==", + "dev": true, + "requires": { + "@angular-devkit/core": "14.2.1", + "@angular-devkit/schematics": "14.2.1", + "jsonc-parser": "3.1.0" + }, + "dependencies": { + "@angular-devkit/core": { + "version": "14.2.1", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-14.2.1.tgz", + "integrity": "sha512-lW8oNGuJqr4r31FWBjfWQYkSXdiOHBGOThIEtHvUVBKfPF/oVrupLueCUgBPel+NvxENXdo93uPsqHN7bZbmsQ==", + "dev": true, + "requires": { + "ajv": "8.11.0", + "ajv-formats": "2.1.1", + "jsonc-parser": "3.1.0", + "rxjs": "6.6.7", + "source-map": "0.7.4" + } + }, + "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 + } + } + }, + "@sideway/address": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz", + "integrity": "sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==", + "dev": true, + "requires": { + "@hapi/hoek": "^9.0.0" + } + }, + "@sideway/formula": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", + "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==", + "dev": true + }, + "@sideway/pinpoint": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", + "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==", + "dev": true + }, + "@sinclair/typebox": { + "version": "0.24.40", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.40.tgz", + "integrity": "sha512-Xint60L8rF0+nRy+6fCjW9jQMmu7fTpbwTBrXZiK6eq/RHDJS7LvWX/0oXC8O7fCePmrY/XdfaTv2HiUDeCq4g==", + "dev": true + }, + "@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": "9.1.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz", + "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==", + "dev": true, + "requires": { + "@sinonjs/commons": "^1.7.0" + } + }, + "@socket.io/component-emitter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz", + "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==", + "dev": true + }, + "@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true + }, + "@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "@tsconfig/node16": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.3.tgz", + "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", + "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.18.1", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.18.1.tgz", + "integrity": "sha512-FSdLaZh2UxaMuLp9lixWaHq/golWTRWOnRsAXzDTDSDOQLuZb1nsdCt6pJSPWSEQt2eFZ2YVk3oYhn+1kLMeMA==", + "dev": true, + "requires": { + "@babel/types": "^7.3.0" + } + }, + "@types/body-parser": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", + "integrity": "sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==", + "dev": true, + "requires": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "@types/bonjour": { + "version": "3.5.10", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.10.tgz", + "integrity": "sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/concat-stream": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@types/concat-stream/-/concat-stream-1.6.1.tgz", + "integrity": "sha512-eHE4cQPoj6ngxBZMvVf6Hw7Mh4jMW4U9lpGmS5GBPB9RYxlFg+CHaVN7ErNY4W9XfLIEn20b4VDYaIrbq0q4uA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/connect": { + "version": "3.4.35", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.35.tgz", + "integrity": "sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/connect-history-api-fallback": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz", + "integrity": "sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw==", + "dev": true, + "requires": { + "@types/express-serve-static-core": "*", + "@types/node": "*" + } + }, + "@types/cookie": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.4.1.tgz", + "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==", + "dev": true + }, + "@types/cors": { + "version": "2.8.13", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.13.tgz", + "integrity": "sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/eslint": { + "version": "8.4.6", + "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.6.tgz", + "integrity": "sha512-/fqTbjxyFUaYNO7VcW5g+4npmqVACz1bB7RTHYuLj+PRjw9hrCwrUXVQFpChUS0JsyEFvMZ7U/PfmvWgxJhI9g==", + "dev": true, + "requires": { + "@types/estree": "*", + "@types/json-schema": "*" + } + }, + "@types/eslint-scope": { + "version": "3.7.4", + "resolved": "https://registry.npmjs.org/@types/eslint-scope/-/eslint-scope-3.7.4.tgz", + "integrity": "sha512-9K4zoImiZc3HlIp6AVUDE4CWYx22a+lhSZMYNpbjW04+YF0KWj4pJXnEMjdnFTiQibFFmElcsasJXDbdI/EPhA==", + "dev": true, + "requires": { + "@types/eslint": "*", + "@types/estree": "*" + } + }, + "@types/estree": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz", + "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==", + "dev": true + }, + "@types/expect": { + "version": "1.20.4", + "resolved": "https://registry.npmjs.org/@types/expect/-/expect-1.20.4.tgz", + "integrity": "sha512-Q5Vn3yjTDyCMV50TB6VRIbQNxSE4OmZR86VSbGaNpfUolm0iePBB4KdEEHmxoY5sT2+2DIvXW0rvMDP2nHZ4Mg==", + "dev": true + }, + "@types/express": { + "version": "4.17.13", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.13.tgz", + "integrity": "sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==", + "dev": true, + "requires": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^4.17.18", + "@types/qs": "*", + "@types/serve-static": "*" + } + }, + "@types/express-serve-static-core": { + "version": "4.17.30", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.30.tgz", + "integrity": "sha512-gstzbTWro2/nFed1WXtf+TtrpwxH7Ggs4RLYTLbeVgIkUQOI3WG/JKjgeOU1zXDvezllupjrf8OPIdvTbIaVOQ==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*" + } + }, + "@types/form-data": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-0.0.33.tgz", + "integrity": "sha512-8BSvG1kGm83cyJITQMZSulnl6QV8jqAGreJsc5tPu1Jq0vTSOiY/k24Wx82JRpWwZSqrala6sd5rWi6aNXvqcw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@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/http-proxy": { + "version": "1.17.9", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.9.tgz", + "integrity": "sha512-QsbSjA/fSk7xB+UXlCT3wHBy5ai9wOcNDWwZAtud+jXhwOM3l+EYZh8Lng4+/6n8uar0J7xILzqftJdJ/Wdfkw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@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": "28.1.8", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-28.1.8.tgz", + "integrity": "sha512-8TJkV++s7B6XqnDrzR1m/TT0A0h948Pnl/097veySPN67VRAgQ4gZ7n2KfJo2rVq6njQjdxU3GCCyDvAeuHoiw==", + "dev": true, + "requires": { + "expect": "^28.0.0", + "pretty-format": "^28.0.0" + } + }, + "@types/jsdom": { + "version": "16.2.15", + "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-16.2.15.tgz", + "integrity": "sha512-nwF87yjBKuX/roqGYerZZM0Nv1pZDMAT5YhOHYeM/72Fic+VEqJh4nyoqoapzJnW3pUlfxPY5FhgsJtM+dRnQQ==", + "dev": true, + "requires": { + "@types/node": "*", + "@types/parse5": "^6.0.3", + "@types/tough-cookie": "*" + } + }, + "@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": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, + "@types/mime": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz", + "integrity": "sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA==", + "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.56", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.11.56.tgz", + "integrity": "sha512-aFcUkv7EddxxOa/9f74DINReQ/celqH8DiB3fRYgVDM2Xm5QJL8sl80QKuAnGvwAsMn+H3IFA6WCrQh1CY7m1A==", + "dev": true + }, + "@types/normalize-package-data": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", + "integrity": "sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==", + "dev": true + }, + "@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/parse5": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/parse5/-/parse5-6.0.3.tgz", + "integrity": "sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g==", + "dev": true + }, + "@types/prettier": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.0.tgz", + "integrity": "sha512-RI1L7N4JnW5gQw2spvL7Sllfuf1SaHdrZpCHiBlCXjIlufi1SMNnbu2teze3/QE67Fg2tBlH7W+mi4hVNk4p0A==", + "dev": true + }, + "@types/qs": { + "version": "6.9.7", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz", + "integrity": "sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==", + "dev": true + }, + "@types/range-parser": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.4.tgz", + "integrity": "sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==", + "dev": true + }, + "@types/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", + "dev": true + }, + "@types/semver": { + "version": "7.5.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.0.tgz", + "integrity": "sha512-G8hZ6XJiHnuhQKR7ZmysCeJWE08o8T0AXtk5darsCaTVsYZhhgUrq53jizaR2FvsoeCwJhlmwTjkXBY5Pn/ZHw==", + "dev": true + }, + "@types/serve-index": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.1.tgz", + "integrity": "sha512-d/Hs3nWDxNL2xAczmOVZNj92YZCS6RGxfBPjKzuu/XirCgXdpKEb88dYNbrYGint6IVWLNP+yonwVAuRC0T2Dg==", + "dev": true, + "requires": { + "@types/express": "*" + } + }, + "@types/serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-z5xyF6uh8CbjAu9760KDKsH2FcDxZ2tFCsA4HIMWE6IkiYMXfVoa+4f9KX+FN0ZLsaMw1WNG2ETLA6N+/YA+cg==", + "dev": true, + "requires": { + "@types/mime": "*", + "@types/node": "*" + } + }, + "@types/sinonjs__fake-timers": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-8.1.1.tgz", + "integrity": "sha512-0kSuKjAS0TrGLJ0M/+8MaFkGsQhZpB6pxOmvS3K8FYI72K//YmdfoW9X2qPsAKh1mkwxGD5zib9s1FIFed6E8g==", + "dev": true + }, + "@types/sizzle": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.3.tgz", + "integrity": "sha512-JYM8x9EGF163bEyhdJBpR2QX1R5naCJHC8ucJylJ3w9/CVBaskdQ8WqBf8MmQrd1kRvp/a4TS8HJ+bxzR7ZJYQ==", + "dev": true + }, + "@types/sockjs": { + "version": "0.3.33", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", + "integrity": "sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@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/tough-cookie": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.2.tgz", + "integrity": "sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==", + "dev": true + }, + "@types/vinyl": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/vinyl/-/vinyl-2.0.6.tgz", + "integrity": "sha512-ayJ0iOCDNHnKpKTgBG6Q6JOnHTj9zFta+3j2b8Ejza0e4cvRyMn0ZoLEmbPrTHe5YYRlDYPvPWVdV4cTaRyH7g==", + "dev": true, + "requires": { + "@types/expect": "^1.20.4", + "@types/node": "*" + } + }, + "@types/ws": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", + "integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "@types/yargs": { + "version": "17.0.12", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.12.tgz", + "integrity": "sha512-Nz4MPhecOFArtm81gFQvQqdV7XYCrWKx5uUt6GNHredFHn1i2mtWqXTON7EPXMtNi1qjtjEM/VCHDhcHsAMLXQ==", + "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/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-Cn6WYCm0tXv8p6k+A8PvbDG763EDpBoTzHdA+Q/MF6H3sapGjCm9NzoaJncJS9tUKSuCoDs9XHxYYsQDgxR6kw==", + "dev": true, + "optional": true, + "requires": { + "@types/node": "*" + } + }, + "@typescript-eslint/eslint-plugin": { + "version": "5.59.7", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.59.7.tgz", + "integrity": "sha512-BL+jYxUFIbuYwy+4fF86k5vdT9lT0CNJ6HtwrIvGh0PhH8s0yy5rjaKH2fDCrz5ITHy07WCzVGNvAmjJh4IJFA==", + "dev": true, + "requires": { + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.59.7", + "@typescript-eslint/type-utils": "5.59.7", + "@typescript-eslint/utils": "5.59.7", + "debug": "^4.3.4", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "dependencies": { + "@typescript-eslint/scope-manager": { + "version": "5.59.7", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.7.tgz", + "integrity": "sha512-FL6hkYWK9zBGdxT2wWEd2W8ocXMu3K94i3gvMrjXpx+koFYdYV7KprKfirpgY34vTGzEPPuKoERpP8kD5h7vZQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.59.7", + "@typescript-eslint/visitor-keys": "5.59.7" + } + }, + "@typescript-eslint/types": { + "version": "5.59.7", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.7.tgz", + "integrity": "sha512-UnVS2MRRg6p7xOSATscWkKjlf/NDKuqo5TdbWck6rIRZbmKpVNTLALzNvcjIfHBE7736kZOFc/4Z3VcZwuOM/A==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "5.59.7", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.7.tgz", + "integrity": "sha512-4A1NtZ1I3wMN2UGDkU9HMBL+TIQfbrh4uS0WDMMpf3xMRursDbqEf1ahh6vAAe3mObt8k3ZATnezwG4pdtWuUQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.59.7", + "@typescript-eslint/visitor-keys": "5.59.7", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/utils": { + "version": "5.59.7", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.59.7.tgz", + "integrity": "sha512-yCX9WpdQKaLufz5luG4aJbOpdXf/fjwGMcLFXZVPUz3QqLirG5QcwwnIHNf8cjLjxK4qtzTO8udUtMQSAToQnQ==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.59.7", + "@typescript-eslint/types": "5.59.7", + "@typescript-eslint/typescript-estree": "5.59.7", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.59.7", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.7.tgz", + "integrity": "sha512-tyN+X2jvMslUszIiYbF0ZleP+RqQsFVpGrKI6e0Eet1w8WmhsAtmzaqm8oM8WJQ1ysLwhnsK/4hYHJjOgJVfQQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.59.7", + "eslint-visitor-keys": "^3.3.0" + } + } + } + }, + "@typescript-eslint/parser": { + "version": "5.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.59.1.tgz", + "integrity": "sha512-nzjFAN8WEu6yPRDizIFyzAfgK7nybPodMNFGNH0M9tei2gYnYszRDqVA0xlnRjkl7Hkx2vYrEdb6fP2a21cG1g==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "5.59.1", + "@typescript-eslint/types": "5.59.1", + "@typescript-eslint/typescript-estree": "5.59.1", + "debug": "^4.3.4" + } + }, + "@typescript-eslint/scope-manager": { + "version": "5.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.1.tgz", + "integrity": "sha512-mau0waO5frJctPuAzcxiNWqJR5Z8V0190FTSqRw1Q4Euop6+zTwHAf8YIXNwDOT29tyUDrQ65jSg9aTU/H0omA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.59.1", + "@typescript-eslint/visitor-keys": "5.59.1" + } + }, + "@typescript-eslint/type-utils": { + "version": "5.59.7", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.59.7.tgz", + "integrity": "sha512-ozuz/GILuYG7osdY5O5yg0QxXUAEoI4Go3Do5xeu+ERH9PorHBPSdvD3Tjp2NN2bNLh1NJQSsQu2TPu/Ly+HaQ==", + "dev": true, + "requires": { + "@typescript-eslint/typescript-estree": "5.59.7", + "@typescript-eslint/utils": "5.59.7", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "dependencies": { + "@typescript-eslint/scope-manager": { + "version": "5.59.7", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.59.7.tgz", + "integrity": "sha512-FL6hkYWK9zBGdxT2wWEd2W8ocXMu3K94i3gvMrjXpx+koFYdYV7KprKfirpgY34vTGzEPPuKoERpP8kD5h7vZQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.59.7", + "@typescript-eslint/visitor-keys": "5.59.7" + } + }, + "@typescript-eslint/types": { + "version": "5.59.7", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.7.tgz", + "integrity": "sha512-UnVS2MRRg6p7xOSATscWkKjlf/NDKuqo5TdbWck6rIRZbmKpVNTLALzNvcjIfHBE7736kZOFc/4Z3VcZwuOM/A==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "5.59.7", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.7.tgz", + "integrity": "sha512-4A1NtZ1I3wMN2UGDkU9HMBL+TIQfbrh4uS0WDMMpf3xMRursDbqEf1ahh6vAAe3mObt8k3ZATnezwG4pdtWuUQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.59.7", + "@typescript-eslint/visitor-keys": "5.59.7", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/utils": { + "version": "5.59.7", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.59.7.tgz", + "integrity": "sha512-yCX9WpdQKaLufz5luG4aJbOpdXf/fjwGMcLFXZVPUz3QqLirG5QcwwnIHNf8cjLjxK4qtzTO8udUtMQSAToQnQ==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.59.7", + "@typescript-eslint/types": "5.59.7", + "@typescript-eslint/typescript-estree": "5.59.7", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.59.7", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.7.tgz", + "integrity": "sha512-tyN+X2jvMslUszIiYbF0ZleP+RqQsFVpGrKI6e0Eet1w8WmhsAtmzaqm8oM8WJQ1ysLwhnsK/4hYHJjOgJVfQQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.59.7", + "eslint-visitor-keys": "^3.3.0" + } + } + } + }, + "@typescript-eslint/types": { + "version": "5.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.59.1.tgz", + "integrity": "sha512-dg0ICB+RZwHlysIy/Dh1SP+gnXNzwd/KS0JprD3Lmgmdq+dJAJnUPe1gNG34p0U19HvRlGX733d/KqscrGC1Pg==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "5.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.59.1.tgz", + "integrity": "sha512-lYLBBOCsFltFy7XVqzX0Ju+Lh3WPIAWxYpmH/Q7ZoqzbscLiCW00LeYCdsUnnfnj29/s1WovXKh2gwCoinHNGA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.59.1", + "@typescript-eslint/visitor-keys": "5.59.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/utils": { + "version": "5.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.29.0.tgz", + "integrity": "sha512-3Eos6uP1nyLOBayc/VUdKZikV90HahXE5Dx9L5YlSd/7ylQPXhLk1BYb29SDgnBnTp+jmSZUU0QxUiyHgW4p7A==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.29.0", + "@typescript-eslint/types": "5.29.0", + "@typescript-eslint/typescript-estree": "5.29.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + }, + "dependencies": { + "@typescript-eslint/scope-manager": { + "version": "5.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.29.0.tgz", + "integrity": "sha512-etbXUT0FygFi2ihcxDZjz21LtC+Eps9V2xVx09zFoN44RRHPrkMflidGMI+2dUs821zR1tDS6Oc9IXxIjOUZwA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.29.0", + "@typescript-eslint/visitor-keys": "5.29.0" + } + }, + "@typescript-eslint/types": { + "version": "5.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.29.0.tgz", + "integrity": "sha512-X99VbqvAXOMdVyfFmksMy3u8p8yoRGITgU1joBJPzeYa0rhdf5ok9S56/itRoUSh99fiDoMtarSIJXo7H/SnOg==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "5.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.29.0.tgz", + "integrity": "sha512-mQvSUJ/JjGBdvo+1LwC+GY2XmSYjK1nAaVw2emp/E61wEVYEyibRHCqm1I1vEKbXCpUKuW4G7u9ZCaZhJbLoNQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.29.0", + "@typescript-eslint/visitor-keys": "5.29.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.29.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.29.0.tgz", + "integrity": "sha512-Hpb/mCWsjILvikMQoZIE3voc9wtQcS0A9FUw3h8bhr9UxBdtI/tw1ZDZUOXHXLOVMedKCH5NxyzATwnU78bWCQ==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.29.0", + "eslint-visitor-keys": "^3.3.0" + } + } + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.59.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.59.1.tgz", + "integrity": "sha512-6waEYwBTCWryx0VJmP7JaM4FpipLsFl9CvYf2foAE8Qh/Y0s+bxWysciwOs0LTBED4JCaNxTZ5rGadB14M6dwA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.59.1", + "eslint-visitor-keys": "^3.3.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 + }, + "@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", + "dev": true + }, + "abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "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 + }, + "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.8.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", + "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "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" + }, + "dependencies": { + "loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + } + } + }, + "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==", + "dev": true, + "requires": { + "debug": "4" + } + }, + "agentkeepalive": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.2.1.tgz", + "integrity": "sha512-Zn4cw2NEqd+9fiSVWMscnjyQ1a8Yfoc5oBajLeo5w+YBHgDUcEBY2hS4YpTz6iN5f/2zQiktcuM6tS8x1p9dpA==", + "dev": true, + "requires": { + "debug": "^4.1.0", + "depd": "^1.1.2", + "humanize-ms": "^1.2.1" + } + }, + "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.11.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", + "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "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-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dev": true, + "requires": { + "ajv": "^8.0.0" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "angular-calendar": { + "version": "0.30.1", + "resolved": "https://registry.npmjs.org/angular-calendar/-/angular-calendar-0.30.1.tgz", + "integrity": "sha512-QNDGmMzDH/Lx0WXya3mug+MYEpEAQHKlErB/ZpE5F1glChFmPuWCV4aLN6sqacMjZW1TybAJ/ES88B6LS7l8zw==", + "requires": { + "@scarf/scarf": "^1.1.1", + "angular-draggable-droppable": "^7.0.0", + "angular-resizable-element": "^6.0.0", + "calendar-utils": "^0.10.1", + "positioning": "^2.0.1", + "tslib": "^2.4.0" + } + }, + "angular-draggable-droppable": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/angular-draggable-droppable/-/angular-draggable-droppable-7.0.0.tgz", + "integrity": "sha512-XEtIx4hsByrwunKPHiqXRcqCPZlP2YF7ZXWgdoBIGiNVg0L/4xx8OcaKVeVsrOrKsN/t/IB4kJqeG45btUcSGA==", + "requires": { + "@mattlewis92/dom-autoscroller": "^2.4.2", + "tslib": "^2.4.0" + } + }, + "angular-resizable-element": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/angular-resizable-element/-/angular-resizable-element-6.0.2.tgz", + "integrity": "sha512-OEWDfI945UqQMpHfRn/XNrGrOZAXEv6Utzqoc83yAYuNIKJXyTpivcrr/IZsQq735QYLLV/OeDzdP5XilLdMDA==", + "requires": { + "tslib": "^2.3.0" + } + }, + "ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "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==", + "dev": true, + "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": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "dev": true + }, + "arch": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/arch/-/arch-2.2.0.tgz", + "integrity": "sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==", + "dev": true + }, + "are-we-there-yet": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", + "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", + "dev": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + } + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "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" + } + }, + "array-differ": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/array-differ/-/array-differ-3.0.0.tgz", + "integrity": "sha512-THtfYS6KtME/yIAhKjZ2ul7XI96lQGHRputJQHO80LAWQnuGP4iCIN8vdMRboGbIEYBwU33q8Tch1os2+X0kMg==", + "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-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 + }, + "arrify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-2.0.1.tgz", + "integrity": "sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==", + "dev": true + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true + }, + "asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "dev": true, + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "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": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==", + "dev": true + }, + "async-each-series": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/async-each-series/-/async-each-series-0.1.1.tgz", + "integrity": "sha512-p4jj6Fws4Iy2m0iCmI2am2ZNZCgbdgE+P8F/8csmn2vx7ixXrO2zGcuNsD46X5uZSVecmkEy/M06X2vG8KD6dQ==", + "dev": true + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, + "at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true + }, + "atomically": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/atomically/-/atomically-1.7.0.tgz", + "integrity": "sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w==", + "dev": true + }, + "autoprefixer": { + "version": "10.4.9", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.9.tgz", + "integrity": "sha512-Uu67eduPEmOeA0vyJby5ghu1AAELCCNSsLAjK+lz6kYzNM5sqnBO36MqfsjhPjQF/BaJM5U/UuFYyl7PavY/wQ==", + "dev": true, + "requires": { + "browserslist": "^4.21.3", + "caniuse-lite": "^1.0.30001394", + "fraction.js": "^4.2.0", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.0", + "postcss-value-parser": "^4.2.0" + } + }, + "available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "dev": true + }, + "aws-sdk": { + "version": "2.1204.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1204.0.tgz", + "integrity": "sha512-H3dRQBdgzAfZ/e/dfiW44fhQrgAuCfIzWhI5y5J9122caI4uZY6TEUd003UXP4nXq2eMfuPWg0bA/mPwbj8RkA==", + "dev": true, + "requires": { + "buffer": "4.9.2", + "events": "1.1.1", + "ieee754": "1.1.13", + "jmespath": "0.16.0", + "querystring": "0.2.0", + "sax": "1.2.1", + "url": "0.10.3", + "util": "^0.12.4", + "uuid": "8.0.0", + "xml2js": "0.4.19" + }, + "dependencies": { + "buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "dev": true, + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", + "dev": true + }, + "uuid": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", + "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==", + "dev": true + } + } + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", + "dev": true + }, + "aws4": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.11.0.tgz", + "integrity": "sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA==", + "dev": true + }, + "axios": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", + "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", + "dev": true, + "requires": { + "follow-redirects": "^1.14.9", + "form-data": "^4.0.0" + }, + "dependencies": { + "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==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + } + } + }, + "babel-jest": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-28.1.3.tgz", + "integrity": "sha512-epUaPOEWMk3cWX0M/sPvCHHCe9fMFAa/9hXEgKP8nFfNl/jlGkE9ucq9NqkZGXLDduCJYS0UvSlPUwC0S+rH6Q==", + "dev": true, + "requires": { + "@jest/transform": "^28.1.3", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^28.1.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.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==", + "dev": true, + "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==", + "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==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "babel-loader": { + "version": "8.2.5", + "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.5.tgz", + "integrity": "sha512-OSiFfH89LrEMiWd4pLNqGz4CwJDtbs2ZVc+iGu2HrkRfPxId9F2anQj38IxWpmRfsUY0aBZYi1EFcd3mhtRMLQ==", + "dev": true, + "requires": { + "find-cache-dir": "^3.3.1", + "loader-utils": "^2.0.0", + "make-dir": "^3.1.0", + "schema-utils": "^2.6.5" + }, + "dependencies": { + "loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + } + } + }, + "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" + } + }, + "babel-plugin-jest-hoist": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-28.1.3.tgz", + "integrity": "sha512-Ys3tUKAmfnkRUpPdpa98eYrAR0nV+sSFUZZEGuQ2EbFd1y4SOLtD5QDNHAq+bb9a+bbXvYQC4b+ID/THIMcU6Q==", + "dev": true, + "requires": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + } + }, + "babel-plugin-polyfill-corejs2": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.3.2.tgz", + "integrity": "sha512-LPnodUl3lS0/4wN3Rb+m+UK8s7lj2jcLRrjho4gLw+OJs+I4bvGXshINesY5xx/apM+biTnQ9reDI8yj+0M5+Q==", + "dev": true, + "requires": { + "@babel/compat-data": "^7.17.7", + "@babel/helper-define-polyfill-provider": "^0.3.2", + "semver": "^6.1.1" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true + } + } + }, + "babel-plugin-polyfill-corejs3": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.3.tgz", + "integrity": "sha512-zKsXDh0XjnrUEW0mxIHLfjBfnXSMr5Q/goMe/fxpQnLm07mcOZiIZHBNWCMx60HmdvjxfXcalac0tfFg0wqxyw==", + "dev": true, + "requires": { + "@babel/helper-define-polyfill-provider": "^0.3.2", + "core-js-compat": "^3.21.0" + } + }, + "babel-plugin-polyfill-regenerator": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.4.0.tgz", + "integrity": "sha512-RW1cnryiADFeHmfLS+WW/G431p1PsW5qdRdz0SDRi7TKcUgc7Oh/uXkT7MZ/+tGsT1BkczEAmD5XjUyJ5SWDTw==", + "dev": true, + "requires": { + "@babel/helper-define-polyfill-provider": "^0.3.2" + } + }, + "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": "28.1.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-28.1.3.tgz", + "integrity": "sha512-L+fupJvlWAHbQfn74coNX3zf60LXMJsezNvvx8eIh7iOR1luJ1poxYgQk1F8PYtNq/6QODDHCqsSnTFSWC491A==", + "dev": true, + "requires": { + "babel-plugin-jest-hoist": "^28.1.3", + "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==" + }, + "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 + }, + "base64id": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", + "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", + "dev": true + }, + "batch": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", + "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", + "dev": true + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "dev": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, + "before-after-hook": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz", + "integrity": "sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ==", + "dev": true + }, + "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 + }, + "bin-links": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/bin-links/-/bin-links-3.0.3.tgz", + "integrity": "sha512-zKdnMPWEdh4F5INR07/eBrodC7QrF5JKvqskjz/ZZRXg5YSAZIbn8zGhbhUrElzHBZ2fvEQdOU59RHcTG3GiwA==", + "dev": true, + "requires": { + "cmd-shim": "^5.0.0", + "mkdirp-infer-owner": "^2.0.0", + "npm-normalize-package-bin": "^2.0.0", + "read-cmd-shim": "^3.0.0", + "rimraf": "^3.0.0", + "write-file-atomic": "^4.0.0" + }, + "dependencies": { + "npm-normalize-package-bin": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-2.0.0.tgz", + "integrity": "sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ==", + "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==" + }, + "binaryextensions": { + "version": "4.18.0", + "resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-4.18.0.tgz", + "integrity": "sha512-PQu3Kyv9dM4FnwB7XGj1+HucW+ShvJzJqjuw1JkKVs1mWdwOKVcRjOi+pV9X52A0tNvrPCsPkbFFQb+wE1EAXw==", + "dev": true + }, + "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" + } + }, + "blob-util": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/blob-util/-/blob-util-2.0.2.tgz", + "integrity": "sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==", + "dev": true + }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true + }, + "body-parser": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.0.tgz", + "integrity": "sha512-DfJ+q6EPcGKZD1QWUjSpqp+Q7bDQTsQIF4zfUAtZ6qk+H/3/QRhg9CEp39ss+/T2vw0+HaidC0ecJj/DRLIaKg==", + "dev": true, + "requires": { + "bytes": "3.1.2", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.10.3", + "raw-body": "2.5.1", + "type-is": "~1.6.18", + "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" + } + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true + }, + "destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, + "qs": { + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", + "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "dev": true, + "requires": { + "side-channel": "^1.0.4" + } + } + } + }, + "bonjour-service": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.0.14.tgz", + "integrity": "sha512-HIMbgLnk1Vqvs6B4Wq5ep7mxvj9sGz5d1JJyDNSGNIdA/w2MCz6GTjWTdjqOJV1bEPj+6IkxDvWNFKEBxNt4kQ==", + "dev": true, + "requires": { + "array-flatten": "^2.1.2", + "dns-equal": "^1.0.0", + "fast-deep-equal": "^3.1.3", + "multicast-dns": "^7.2.5" + } + }, + "boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true + }, + "bootstrap": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.2.0.tgz", + "integrity": "sha512-qlnS9GL6YZE6Wnef46GxGv1UpGGzAwO0aPL1yOjzDIJpeApeMvqV24iL+pjr2kU4dduoBA9fINKWKgMToobx9A==", + "requires": {} + }, + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "requires": { + "balanced-match": "^1.0.0" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "requires": { + "fill-range": "^7.0.1" + } + }, + "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 + }, + "browser-sync": { + "version": "2.28.3", + "resolved": "https://registry.npmjs.org/browser-sync/-/browser-sync-2.28.3.tgz", + "integrity": "sha512-gublDeevvAuypnc01SQNGL8fkm4RdIkEagnAJ8Tl9mvr2td3Pl4nVIg5S6fcgoMDEWb8IT7nUHG9YwTATn/k2g==", + "dev": true, + "requires": { + "browser-sync-client": "^2.28.3", + "browser-sync-ui": "^2.28.3", + "bs-recipes": "1.3.4", + "bs-snippet-injector": "^2.0.1", + "chalk": "4.1.2", + "chokidar": "^3.5.1", + "connect": "3.6.6", + "connect-history-api-fallback": "^1", + "dev-ip": "^1.0.1", + "easy-extender": "^2.3.4", + "eazy-logger": "^4.0.1", + "etag": "^1.8.1", + "fresh": "^0.5.2", + "fs-extra": "3.0.1", + "http-proxy": "^1.18.1", + "immutable": "^3", + "localtunnel": "^2.0.1", + "micromatch": "^4.0.2", + "opn": "5.3.0", + "portscanner": "2.2.0", + "qs": "^6.11.0", + "raw-body": "^2.3.2", + "resp-modifier": "6.0.2", + "rx": "4.1.0", + "send": "0.16.2", + "serve-index": "1.9.1", + "serve-static": "1.13.2", + "server-destroy": "1.0.1", + "socket.io": "^4.4.1", + "ua-parser-js": "^1.0.33", + "yargs": "^17.3.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.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==", + "dev": true, + "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==", + "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==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "browser-sync-client": { + "version": "2.28.3", + "resolved": "https://registry.npmjs.org/browser-sync-client/-/browser-sync-client-2.28.3.tgz", + "integrity": "sha512-SMsnGkyXlySVLBWRrXdnTdtQCy0Sl5UoiF8BVtigj9S49DaPWQiesbsyq+uJBUKgpyNve+cvfpBU3KSfIp6oLQ==", + "dev": true, + "requires": { + "etag": "1.8.1", + "fresh": "0.5.2", + "mitt": "^1.1.3", + "rxjs": "^5.5.6", + "typescript": "^4.6.2" + }, + "dependencies": { + "rxjs": { + "version": "5.5.12", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-5.5.12.tgz", + "integrity": "sha512-xx2itnL5sBbqeeiVgNPVuQQ1nC8Jp2WfNJhXWHmElW9YmrpS9UVnNzhP3EH3HFqexO5Tlp8GhYY+WEcqcVMvGw==", + "dev": true, + "requires": { + "symbol-observable": "1.0.1" + } + }, + "symbol-observable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.0.1.tgz", + "integrity": "sha512-Kb3PrPYz4HanVF1LVGuAdW6LoVgIwjUYJGzFe7NDrBLCN4lsV/5J0MFurV+ygS4bRVwrCEt2c7MQ1R2a72oJDw==", + "dev": true + } + } + }, + "browser-sync-ui": { + "version": "2.28.3", + "resolved": "https://registry.npmjs.org/browser-sync-ui/-/browser-sync-ui-2.28.3.tgz", + "integrity": "sha512-Mj5M+O3jroGp5hlO6pDfUo19wzUTIuvGyzaRrJAYUgsSkpFacrX+MLCjN9VbZm9fYXbtHyIsnIUUIlYag87wgQ==", + "dev": true, + "requires": { + "async-each-series": "0.1.1", + "chalk": "4.1.2", + "connect-history-api-fallback": "^1", + "immutable": "^3", + "server-destroy": "1.0.1", + "socket.io-client": "^4.4.1", + "stream-throttle": "^0.1.3" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.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==", + "dev": true, + "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==", + "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==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "browser-sync-webpack-plugin": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/browser-sync-webpack-plugin/-/browser-sync-webpack-plugin-2.3.0.tgz", + "integrity": "sha512-MDvuRrTCtoL11dTdwMymo9CNJvYxJoW67gOO61cThfzHNX40S5WcBU+0bVQ86ll7r7aNpNgyzxF7RtnXMTDbyA==", + "dev": true, + "requires": { + "lodash": "^4" + } + }, + "browserslist": { + "version": "4.21.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.3.tgz", + "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==", + "requires": { + "caniuse-lite": "^1.0.30001370", + "electron-to-chromium": "^1.4.202", + "node-releases": "^2.0.6", + "update-browserslist-db": "^1.0.5" + } + }, + "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" + } + }, + "bs-recipes": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/bs-recipes/-/bs-recipes-1.3.4.tgz", + "integrity": "sha512-BXvDkqhDNxXEjeGM8LFkSbR+jzmP/CYpCiVKYn+soB1dDldeU15EBNDkwVXndKuX35wnNUaPd0qSoQEAkmQtMw==", + "dev": true + }, + "bs-snippet-injector": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/bs-snippet-injector/-/bs-snippet-injector-2.0.1.tgz", + "integrity": "sha512-4u8IgB+L9L+S5hknOj3ddNSb42436gsnGm1AuM15B7CdbkpQTyVWgIM5/JUBiKiRwGOR86uo0Lu/OsX+SAlJmw==", + "dev": true + }, + "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": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true + }, + "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==", + "dev": true + }, + "builtins": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-5.0.1.tgz", + "integrity": "sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==", + "dev": true, + "requires": { + "semver": "^7.0.0" + } + }, + "bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true + }, + "cacache": { + "version": "16.1.2", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-16.1.2.tgz", + "integrity": "sha512-Xx+xPlfCZIUHagysjjOAje9nRo8pRDczQCcXb4J2O0BLtH+xeVue6ba4y1kfJfQMAnM2mkcoMIAyOctlaRGWYA==", + "dev": true, + "requires": { + "@npmcli/fs": "^2.1.0", + "@npmcli/move-file": "^2.0.0", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "glob": "^8.0.1", + "infer-owner": "^1.0.4", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "mkdirp": "^1.0.4", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^9.0.0", + "tar": "^6.1.11", + "unique-filename": "^1.1.1" + } + }, + "cachedir": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cachedir/-/cachedir-2.3.0.tgz", + "integrity": "sha512-A+Fezp4zxnit6FanDmv9EqXNAi3vt9DWp51/71UEhXukb7QUuvtv9344h91dyAxuTLoSYJFU299qzR3tzwPAhw==", + "dev": true + }, + "calendar-utils": { + "version": "0.10.4", + "resolved": "https://registry.npmjs.org/calendar-utils/-/calendar-utils-0.10.4.tgz", + "integrity": "sha512-gBK4xCJ42yjaUKwuUha6cZOfxAmGzvSgbdAaX3xLRioeKbYoOK1x1qeD6dch72rsMZlTgATPbBBx42bnkStqgQ==" + }, + "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-lite": { + "version": "1.0.30001397", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001397.tgz", + "integrity": "sha512-SW9N2TbCdLf0eiNDRrrQXx2sOkaakNZbCjgNpPyMJJbiOrU5QzMIrXOVMRM1myBXTD5iTkdrtU/EguCrBocHlA==" + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.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==", + "dev": true + }, + "chart.js": { + "version": "3.9.1", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-3.9.1.tgz", + "integrity": "sha512-Ro2JbLmvg83gXF5F4sniaQ+lTbSv18E+TIf2cOeiH1Iqd2PGFOtem+DUufMZsCJwFE7ywPOpfXFBwRTGq7dh6w==", + "peer": true + }, + "check-more-types": { + "version": "2.24.0", + "resolved": "https://registry.npmjs.org/check-more-types/-/check-more-types-2.24.0.tgz", + "integrity": "sha512-Pj779qHxV2tuapviy1bSZNEL1maXr13bPYpsvSDB68HlYcYuhlDrmGd63i0JHMCLKzc7rUSNIrpdJlhVlNwrxA==", + "dev": true + }, + "chevrotain": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-10.3.0.tgz", + "integrity": "sha512-sy3yTBfvNJmxzYOGWaDStZFuA7va5/MXwzBU+XBIol0bR0aYlfGqTjzgedeu32Ki/j7IVyvYAZO2PInBjmmMjg==", + "dev": true, + "requires": { + "@chevrotain/cst-dts-gen": "10.3.0", + "@chevrotain/gast": "10.3.0", + "@chevrotain/types": "10.3.0", + "@chevrotain/utils": "10.3.0", + "lodash": "4.17.21", + "regexp-to-ast": "0.5.0" + } + }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "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.4.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.4.0.tgz", + "integrity": "sha512-t5QdPT5jq3o262DOQ8zA6E1tlH2upmUc4Hlvrbx1pGYJuiiHl7O7rvVNI+l8HTVhd/q3Qc9vqimkNk5yiXsAug==", + "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 + }, + "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-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "requires": { + "restore-cursor": "^3.1.0" + } + }, + "cli-spinners": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz", + "integrity": "sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==", + "dev": true + }, + "cli-table": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/cli-table/-/cli-table-0.3.11.tgz", + "integrity": "sha512-IqLQi4lO0nIB4tcdTpN4LCB9FI3uqrJZK7RC515EnhZ6qBaglkIgICb1wjeAqpdoOabm1+SuQtkXIPdYC93jhQ==", + "dev": true, + "requires": { + "colors": "1.0.3" + } + }, + "cli-table3": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.2.tgz", + "integrity": "sha512-QyavHCaIC80cMivimWu4aWHilIpiDpfm3hGmqAmXVL1UsnbLuBSMd21hTX6VY4ZSDSM73ESLeF8TOYId3rBTbw==", + "dev": true, + "requires": { + "@colors/colors": "1.5.0", + "string-width": "^4.2.0" + } + }, + "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==", + "dev": true + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "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": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true + }, + "clone-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", + "integrity": "sha512-KLLTJWrvwIP+OPfMn0x2PheDEP20RPUcGXj/ERegTgdmPEZylALQldygiqrPPu8P45uNuPs7ckmReLY6v/iA5g==", + "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-stats": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", + "integrity": "sha512-au6ydSpg6nsrigcZ4m8Bc9hxjeW+GJ8xh5G3BJCMt4WXe1H10UNaVOamqQTmrx1kjVuxAHIQSNU6hY4Nsn9/ag==", + "dev": true + }, + "cloneable-readable": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz", + "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==", + "dev": true, + "requires": { + "inherits": "^2.0.1", + "process-nextick-args": "^2.0.0", + "readable-stream": "^2.3.5" + }, + "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" + } + } + } + }, + "cmd-shim": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cmd-shim/-/cmd-shim-5.0.0.tgz", + "integrity": "sha512-qkCtZ59BidfEwHltnJwkyVZn+XQojdAySM1D1gSeh11Z4pW1Kpolkyo53L5noc0nrxmIvyFwTmJRo4xs7FFLPw==", + "dev": true, + "requires": { + "mkdirp-infer-owner": "^2.0.0" + } + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", + "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 + }, + "color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dev": true, + "requires": { + "color-convert": "^1.9.3", + "color-string": "^1.6.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==", + "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": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dev": true, + "requires": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true + }, + "colorette": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.19.tgz", + "integrity": "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==", + "dev": true + }, + "colors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz", + "integrity": "sha512-pFGrxThWcWQ2MsAz6RtgeWe4NK2kUE1WfsrvvlctdII745EW9I0yflqhe7++M5LEc7bV2c/9/5zc8sFcpL0Drw==", + "dev": true + }, + "colorspace": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", + "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", + "dev": true, + "requires": { + "color": "^3.1.3", + "text-hex": "1.0.x" + } + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", + "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "dev": true + }, + "common-ancestor-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/common-ancestor-path/-/common-ancestor-path-1.0.1.tgz", + "integrity": "sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==", + "dev": true + }, + "common-tags": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", + "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==", + "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": { + "bytes": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz", + "integrity": "sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==", + "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": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + } + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "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==", + "dev": true, + "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==", + "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" + } + } + } + }, + "concurrently": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-8.2.0.tgz", + "integrity": "sha512-nnLMxO2LU492mTUj9qX/az/lESonSZu81UznYDoXtz1IQf996ixVqPAgHXwvHiHCAef/7S8HIK+fTFK7Ifk8YA==", + "dev": true, + "requires": { + "chalk": "^4.1.2", + "date-fns": "^2.30.0", + "lodash": "^4.17.21", + "rxjs": "^7.8.1", + "shell-quote": "^1.8.1", + "spawn-command": "0.0.2", + "supports-color": "^8.1.1", + "tree-kill": "^1.2.2", + "yargs": "^17.7.2" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.5.tgz", + "integrity": "sha512-ecjvYlnAaZ/KVneE/OdKYBYfgXV3Ptu6zQWmgEF7vwKhQnvVS6bjMD2XYgj+SNvQ1GfK/pjgokfPkC/2CO8CuA==", + "dev": true, + "requires": { + "regenerator-runtime": "^0.13.11" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.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==", + "dev": true, + "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==", + "dev": true + }, + "date-fns": { + "version": "2.30.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", + "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", + "dev": true, + "requires": { + "@babel/runtime": "^7.21.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "dev": true + }, + "rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "requires": { + "tslib": "^2.1.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" + } + }, + "yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "requires": { + "cliui": "^8.0.1", + "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.1.1" + } + } + } + }, + "conf": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/conf/-/conf-10.2.0.tgz", + "integrity": "sha512-8fLl9F04EJqjSqH+QjITQfJF8BrOVaYr1jewVgSRAEWePfxT0sku4w2hrGQ60BC/TNLGQ2pgxNlTbWQmMPFvXg==", + "dev": true, + "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" + } + }, + "connect": { + "version": "3.6.6", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.6.6.tgz", + "integrity": "sha512-OO7axMmPpu/2XuX1+2Yrg0ddju31B6xLZMWkJ5rYBu4YRmRVlOjvlY6kw2FJKiAzyxGwnrDUAG4s1Pf0sbBMCQ==", + "dev": true, + "requires": { + "debug": "2.6.9", + "finalhandler": "1.1.0", + "parseurl": "~1.3.2", + "utils-merge": "1.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" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + } + } + }, + "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 + }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "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==", + "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": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==", + "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-webpack-plugin": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz", + "integrity": "sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==", + "dev": true, + "requires": { + "fast-glob": "^3.2.11", + "glob-parent": "^6.0.1", + "globby": "^13.1.1", + "normalize-path": "^3.0.0", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0" + }, + "dependencies": { + "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" + } + }, + "globby": { + "version": "13.1.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.2.tgz", + "integrity": "sha512-LKSDZXToac40u8Q1PQtZihbNdTYSNMuWe+K5l+oa6KgDzSvVrHXlJy40hUP522RjAIoNLJYBJi7ow+rbFpIhHQ==", + "dev": true, + "requires": { + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.11", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^4.0.0" + } + }, + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } + }, + "slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true + } + } + }, + "core-js-compat": { + "version": "3.25.1", + "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.25.1.tgz", + "integrity": "sha512-pOHS7O0i8Qt4zlPW/eIFjwp+NrTPx+wTL0ctgI2fHn31sZOq89rDsmtc/A2vAX7r6shl+bmVI+678He46jgBlw==", + "dev": true, + "requires": { + "browserslist": "^4.21.3" + } + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "dev": true + }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dev": true, + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, + "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" + }, + "dependencies": { + "yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "dev": true + } + } + }, + "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.16", + "resolved": "https://registry.npmjs.org/critters/-/critters-0.0.16.tgz", + "integrity": "sha512-JwjgmO6i3y6RWtLYmXwO5jMd+maZt8Tnfu7VVISmEWyQqfLpB8soBswf8/2bu6SBXxtKA68Al3c+qIG1ApT68A==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "css-select": "^4.2.0", + "parse5": "^6.0.1", + "parse5-htmlparser2-tree-adapter": "^6.0.1", + "postcss": "^8.3.7", + "pretty-bytes": "^5.3.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.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==", + "dev": true, + "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==", + "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==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "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" + } + }, + "css-blank-pseudo": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/css-blank-pseudo/-/css-blank-pseudo-3.0.3.tgz", + "integrity": "sha512-VS90XWtsHGqoM0t4KpH053c4ehxZ2E6HtGI7x68YFV0pTo/QmkV/YFA+NnlvK8guxZVNWGQhVNJGC39Q8XF4OQ==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.9" + } + }, + "css-has-pseudo": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/css-has-pseudo/-/css-has-pseudo-3.0.4.tgz", + "integrity": "sha512-Vse0xpR1K9MNlp2j5w1pgWIJtm1a8qS0JwS9goFYcImjlHEmywP9VUF05aGBXzGpDJF86QXk4L0ypBmwPhGArw==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.9" + } + }, + "css-loader": { + "version": "6.7.1", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.7.1.tgz", + "integrity": "sha512-yB5CNFa14MbPJcomwNh3wLThtkZgcNyI2bNMRt8iE5Z8Vwl7f8vQXFAzn2HDOJvtDq2NTZBUGMSUNNyrv3/+cw==", + "dev": true, + "requires": { + "icss-utils": "^5.1.0", + "postcss": "^8.4.7", + "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.2.0", + "semver": "^7.3.5" + } + }, + "css-prefers-color-scheme": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz", + "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==", + "dev": true, + "requires": {} + }, + "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-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": "7.0.1", + "resolved": "https://registry.npmjs.org/cssdb/-/cssdb-7.0.1.tgz", + "integrity": "sha512-pT3nzyGM78poCKLAEy2zWIVX2hikq6dIrjuZzLV98MumBg+xMTNYfHx7paUlfiRTgg91O/vR889CIf+qiv79Rw==", + "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 + }, + "cssom": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", + "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 + } + } + }, + "cypress": { + "version": "12.16.0", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-12.16.0.tgz", + "integrity": "sha512-mwv1YNe48hm0LVaPgofEhGCtLwNIQEjmj2dJXnAkY1b4n/NE9OtgPph4TyS+tOtYp5CKtRmDvBzWseUXQTjbTg==", + "dev": true, + "requires": { + "@cypress/request": "^2.88.10", + "@cypress/xvfb": "^1.2.4", + "@types/node": "^14.14.31", + "@types/sinonjs__fake-timers": "8.1.1", + "@types/sizzle": "^2.3.2", + "arch": "^2.2.0", + "blob-util": "^2.0.2", + "bluebird": "^3.7.2", + "buffer": "^5.6.0", + "cachedir": "^2.3.0", + "chalk": "^4.1.0", + "check-more-types": "^2.24.0", + "cli-cursor": "^3.1.0", + "cli-table3": "~0.6.1", + "commander": "^6.2.1", + "common-tags": "^1.8.0", + "dayjs": "^1.10.4", + "debug": "^4.3.4", + "enquirer": "^2.3.6", + "eventemitter2": "6.4.7", + "execa": "4.1.0", + "executable": "^4.1.1", + "extract-zip": "2.0.1", + "figures": "^3.2.0", + "fs-extra": "^9.1.0", + "getos": "^3.2.1", + "is-ci": "^3.0.0", + "is-installed-globally": "~0.4.0", + "lazy-ass": "^1.6.0", + "listr2": "^3.8.3", + "lodash": "^4.17.21", + "log-symbols": "^4.0.0", + "minimist": "^1.2.8", + "ospath": "^1.2.2", + "pretty-bytes": "^5.6.0", + "proxy-from-env": "1.0.0", + "request-progress": "^3.0.0", + "semver": "^7.3.2", + "supports-color": "^8.1.1", + "tmp": "~0.2.1", + "untildify": "^4.0.0", + "yauzl": "^2.10.0" + }, + "dependencies": { + "@types/node": { + "version": "14.18.28", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.28.tgz", + "integrity": "sha512-CK2fnrQlIgKlCV3N2kM+Gznb5USlwA1KFX3rJVHmgVk6NJxFPuQ86pAcvKnu37IA4BGlSRz7sEE1lHL1aLZ/eQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "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==", + "dev": true, + "requires": { + "has-flag": "^4.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==", + "dev": true, + "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==", + "dev": true + }, + "fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.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" + } + }, + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "dev": true + } + } + }, + "dargs": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz", + "integrity": "sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==", + "dev": true + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "data-urls": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", + "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", + "dev": true, + "requires": { + "abab": "^2.0.6", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0" + }, + "dependencies": { + "whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "dev": true, + "requires": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + } + } + } + }, + "date-fns": { + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-1.30.1.tgz", + "integrity": "sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==" + }, + "dateformat": { + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz", + "integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==", + "dev": true + }, + "dayjs": { + "version": "1.11.5", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.5.tgz", + "integrity": "sha512-CAdX5Q3YW3Gclyo5Vpqkgpj8fSdLQcRuzfX6mC6Phy0nfJ0eGYOeS7m4mt2plDWLAtA4TqTakvbboHvUxfe4iA==" + }, + "debounce-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/debounce-fn/-/debounce-fn-4.0.0.tgz", + "integrity": "sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ==", + "dev": true, + "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" + } + }, + "debuglog": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/debuglog/-/debuglog-1.0.1.tgz", + "integrity": "sha512-syBZ+rnAK3EgMsH2aYEOLUW7mZSY9Gb+0wUMCFsZvcmiz+HigA0LOcq/HoQqVuGG+EKykunc7QG2bzrponfaSw==", + "dev": true + }, + "decimal.js": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.0.tgz", + "integrity": "sha512-Nv6ENEzyPQ6AItkGwLE2PGKinZZ9g59vSh2BeH6NqPu0OTKZ5ruJsVqh/orbAnqXc9pBbgXAIrc2EyaCj8NpGg==", + "dev": true + }, + "dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", + "dev": true + }, + "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": "6.0.3", + "resolved": "https://registry.npmjs.org/default-gateway/-/default-gateway-6.0.3.tgz", + "integrity": "sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==", + "dev": true, + "requires": { + "execa": "^5.0.0" + }, + "dependencies": { + "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" + } + }, + "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 + }, + "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 + } + } + }, + "defaults": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", + "integrity": "sha512-s82itHOnYrN0Ib8r+z7laQz3sdE+4FP3d9Q7VLO7U+KRT+CR0GsWuyHxzdAY82I7cXv0G/twrqomTJLOssO5HA==", + "dev": true, + "requires": { + "clone": "^1.0.2" + } + }, + "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.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "dev": true, + "requires": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true + }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "dev": true + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "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==" + }, + "deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", + "dev": true + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha512-3NdhDuEXnfun/z7x9GOElY49LoqVHoGScmOKwmxhsS8N5Y+Z8KyPPDnaSzqWgYt/ji4mqwfTS34Htrk0zPIXVg==", + "dev": true + }, + "detect-indent": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-7.0.1.tgz", + "integrity": "sha512-Mc7QhQ8s+cLrnUfU/Ji94vG/r8M26m8f++vyres4ZoojaRDpZ1eSIh/EpzLNwlWuvzSZ3UbDFspjFvTDXe6e/g==", + "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==", + "dev": true + }, + "dev-ip": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dev-ip/-/dev-ip-1.0.1.tgz", + "integrity": "sha512-LmVkry/oDShEgSZPNgqCIp2/TlqtExeGmymru3uCELnfyjY11IzpAproLYs+1X88fXO6DBoYP3ul2Xo2yz2j6A==", + "dev": true + }, + "dezalgo": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", + "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", + "dev": true, + "requires": { + "asap": "^2.0.0", + "wrappy": "1" + } + }, + "didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": 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": "28.1.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-28.1.1.tgz", + "integrity": "sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==", + "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": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==", + "dev": true + }, + "dns-packet": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.4.0.tgz", + "integrity": "sha512-EgqGeaBB8hLiHLZtp/IbaDQTL8pZ0+IvwzSHA6d7VyMDM+B9hgddEMa9xjK5oYnw0ci0JQ6g2XCD7/f6cafU6g==", + "dev": true, + "requires": { + "@leichtgewicht/ip-codec": "^2.0.1" + } + }, + "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.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dev": true, + "requires": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + } + }, + "domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true + }, + "domexception": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", + "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", + "dev": true, + "requires": { + "webidl-conversions": "^7.0.0" + } + }, + "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": "6.0.1", + "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz", + "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==", + "dev": true, + "requires": { + "is-obj": "^2.0.0" + } + }, + "drange": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/drange/-/drange-1.1.1.tgz", + "integrity": "sha512-pYxfDYpued//QpnLIm4Avk7rsNtAtQkUES2cwAYSvD/wd2pKD71gN2Ebj3e7klzXwjocvE8c5vx/1fxwpqmSxA==", + "dev": true + }, + "duplexer": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "dev": true + }, + "eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "easy-extender": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/easy-extender/-/easy-extender-2.3.4.tgz", + "integrity": "sha512-8cAwm6md1YTiPpOvDULYJL4ZS6WfM5/cTeVVh4JsvyYZAoqlRVUpHL9Gr5Fy7HA6xcSZicUia3DeAgO3Us8E+Q==", + "dev": true, + "requires": { + "lodash": "^4.17.10" + } + }, + "eazy-logger": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/eazy-logger/-/eazy-logger-4.0.1.tgz", + "integrity": "sha512-2GSFtnnC6U4IEKhEI7+PvdxrmjJ04mdsj3wHZTFiw0tUtG4HCWzTr13ZYTk8XOGnA1xQMaDljoBOYlk3D/MMSw==", + "dev": true, + "requires": { + "chalk": "4.1.2" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.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==", + "dev": true, + "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==", + "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==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "dev": true, + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true + }, + "ejs": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.8.tgz", + "integrity": "sha512-/sXZeMlhS0ArkfX2Aw780gJzXSMPnKjtspYZv+f3NiKLlubezAHDU5+9xz6gd3/NhG3txQCo6xlglmTS+oTGEQ==", + "dev": true, + "requires": { + "jake": "^10.8.5" + } + }, + "electron-to-chromium": { + "version": "1.4.247", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.247.tgz", + "integrity": "sha512-FLs6R4FQE+1JHM0hh3sfdxnYjKvJpHZyhQDjc2qFq/xFvmmRt/TATNToZhrcGUFzpF2XjeiuozrA8lI0PZmYYw==" + }, + "emittery": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.2.tgz", + "integrity": "sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==", + "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 + }, + "enabled": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", + "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", + "dev": true + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "dev": true + }, + "encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "dev": true, + "optional": true, + "requires": { + "iconv-lite": "^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, + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + } + } + }, + "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==", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "engine.io": { + "version": "6.4.2", + "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.4.2.tgz", + "integrity": "sha512-FKn/3oMiJjrOEOeUub2WCox6JhxBXq/Zn3fZOMCBxKnNYtsdKjxhl7yR3fZhM9PV+rdE75SU5SYMc+2PGzo+Tg==", + "dev": true, + "requires": { + "@types/cookie": "^0.4.1", + "@types/cors": "^2.8.12", + "@types/node": ">=10.0.0", + "accepts": "~1.3.4", + "base64id": "2.0.0", + "cookie": "~0.4.1", + "cors": "~2.8.5", + "debug": "~4.3.1", + "engine.io-parser": "~5.0.3", + "ws": "~8.11.0" + } + }, + "engine.io-client": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.4.0.tgz", + "integrity": "sha512-GyKPDyoEha+XZ7iEqam49vz6auPnNJ9ZBfy89f+rMMas8AuiMWOZ9PVzu8xb9ZC6rafUqiGHSCfu22ih66E+1g==", + "dev": true, + "requires": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.0.3", + "ws": "~8.11.0", + "xmlhttprequest-ssl": "~2.0.0" + } + }, + "engine.io-parser": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.0.4.tgz", + "integrity": "sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg==", + "dev": true + }, + "enhanced-resolve": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz", + "integrity": "sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + } + }, + "enquirer": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", + "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "dev": true, + "requires": { + "ansi-colors": "^4.1.1" + } + }, + "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==", + "dev": true + }, + "err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "dev": true + }, + "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, + "optional": true, + "requires": { + "prr": "~1.0.1" + } + }, + "error": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/error/-/error-10.4.0.tgz", + "integrity": "sha512-YxIFEJuhgcICugOUvRx5th0UM+ActZ9sjY0QJmeVwsQdvosZ7kYzc9QqS0Da3R5iUmgU5meGIxh0xBeZpMVeLw==", + "dev": true + }, + "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.20.2", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.2.tgz", + "integrity": "sha512-XxXQuVNrySBNlEkTYJoDNFe5+s2yIOpzq80sUHEdPdQr0S5nTLz4ZPPPswNIpKseDDUS5yghX1gfLIHQZ1iNuQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.1.2", + "get-symbol-description": "^1.0.0", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "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.2", + "is-string": "^1.0.7", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.2", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.4.3", + "string.prototype.trimend": "^1.0.5", + "string.prototype.trimstart": "^1.0.5", + "unbox-primitive": "^1.0.2" + } + }, + "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 + }, + "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" + } + }, + "esbuild": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.15.5.tgz", + "integrity": "sha512-VSf6S1QVqvxfIsSKb3UKr3VhUCis7wgDbtF4Vd9z84UJr05/Sp2fRKmzC+CSPG/dNAPPJZ0BTBLTT1Fhd6N9Gg==", + "dev": true, + "optional": true, + "requires": { + "@esbuild/linux-loong64": "0.15.5", + "esbuild-android-64": "0.15.5", + "esbuild-android-arm64": "0.15.5", + "esbuild-darwin-64": "0.15.5", + "esbuild-darwin-arm64": "0.15.5", + "esbuild-freebsd-64": "0.15.5", + "esbuild-freebsd-arm64": "0.15.5", + "esbuild-linux-32": "0.15.5", + "esbuild-linux-64": "0.15.5", + "esbuild-linux-arm": "0.15.5", + "esbuild-linux-arm64": "0.15.5", + "esbuild-linux-mips64le": "0.15.5", + "esbuild-linux-ppc64le": "0.15.5", + "esbuild-linux-riscv64": "0.15.5", + "esbuild-linux-s390x": "0.15.5", + "esbuild-netbsd-64": "0.15.5", + "esbuild-openbsd-64": "0.15.5", + "esbuild-sunos-64": "0.15.5", + "esbuild-windows-32": "0.15.5", + "esbuild-windows-64": "0.15.5", + "esbuild-windows-arm64": "0.15.5" + } + }, + "esbuild-android-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-android-64/-/esbuild-android-64-0.15.5.tgz", + "integrity": "sha512-dYPPkiGNskvZqmIK29OPxolyY3tp+c47+Fsc2WYSOVjEPWNCHNyqhtFqQadcXMJDQt8eN0NMDukbyQgFcHquXg==", + "dev": true, + "optional": true + }, + "esbuild-android-arm64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-android-arm64/-/esbuild-android-arm64-0.15.5.tgz", + "integrity": "sha512-YyEkaQl08ze3cBzI/4Cm1S+rVh8HMOpCdq8B78JLbNFHhzi4NixVN93xDrHZLztlocEYqi45rHHCgA8kZFidFg==", + "dev": true, + "optional": true + }, + "esbuild-darwin-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.5.tgz", + "integrity": "sha512-Cr0iIqnWKx3ZTvDUAzG0H/u9dWjLE4c2gTtRLz4pqOBGjfjqdcZSfAObFzKTInLLSmD0ZV1I/mshhPoYSBMMCQ==", + "dev": true, + "optional": true + }, + "esbuild-darwin-arm64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.5.tgz", + "integrity": "sha512-WIfQkocGtFrz7vCu44ypY5YmiFXpsxvz2xqwe688jFfSVCnUsCn2qkEVDo7gT8EpsLOz1J/OmqjExePL1dr1Kg==", + "dev": true, + "optional": true + }, + "esbuild-freebsd-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.5.tgz", + "integrity": "sha512-M5/EfzV2RsMd/wqwR18CELcenZ8+fFxQAAEO7TJKDmP3knhWSbD72ILzrXFMMwshlPAS1ShCZ90jsxkm+8FlaA==", + "dev": true, + "optional": true + }, + "esbuild-freebsd-arm64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.5.tgz", + "integrity": "sha512-2JQQ5Qs9J0440F/n/aUBNvY6lTo4XP/4lt1TwDfHuo0DY3w5++anw+jTjfouLzbJmFFiwmX7SmUhMnysocx96w==", + "dev": true, + "optional": true + }, + "esbuild-linux-32": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-32/-/esbuild-linux-32-0.15.5.tgz", + "integrity": "sha512-gO9vNnIN0FTUGjvTFucIXtBSr1Woymmx/aHQtuU+2OllGU6YFLs99960UD4Dib1kFovVgs59MTXwpFdVoSMZoQ==", + "dev": true, + "optional": true + }, + "esbuild-linux-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-64/-/esbuild-linux-64-0.15.5.tgz", + "integrity": "sha512-ne0GFdNLsm4veXbTnYAWjbx3shpNKZJUd6XpNbKNUZaNllDZfYQt0/zRqOg0sc7O8GQ+PjSMv9IpIEULXVTVmg==", + "dev": true, + "optional": true + }, + "esbuild-linux-arm": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm/-/esbuild-linux-arm-0.15.5.tgz", + "integrity": "sha512-wvAoHEN+gJ/22gnvhZnS/+2H14HyAxM07m59RSLn3iXrQsdS518jnEWRBnJz3fR6BJa+VUTo0NxYjGaNt7RA7Q==", + "dev": true, + "optional": true + }, + "esbuild-linux-arm64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.5.tgz", + "integrity": "sha512-7EgFyP2zjO065XTfdCxiXVEk+f83RQ1JsryN1X/VSX2li9rnHAt2swRbpoz5Vlrl6qjHrCmq5b6yxD13z6RheA==", + "dev": true, + "optional": true + }, + "esbuild-linux-mips64le": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.5.tgz", + "integrity": "sha512-KdnSkHxWrJ6Y40ABu+ipTZeRhFtc8dowGyFsZY5prsmMSr1ZTG9zQawguN4/tunJ0wy3+kD54GaGwdcpwWAvZQ==", + "dev": true, + "optional": true + }, + "esbuild-linux-ppc64le": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.5.tgz", + "integrity": "sha512-QdRHGeZ2ykl5P0KRmfGBZIHmqcwIsUKWmmpZTOq573jRWwmpfRmS7xOhmDHBj9pxv+6qRMH8tLr2fe+ZKQvCYw==", + "dev": true, + "optional": true + }, + "esbuild-linux-riscv64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.5.tgz", + "integrity": "sha512-p+WE6RX+jNILsf+exR29DwgV6B73khEQV0qWUbzxaycxawZ8NE0wA6HnnTxbiw5f4Gx9sJDUBemh9v49lKOORA==", + "dev": true, + "optional": true + }, + "esbuild-linux-s390x": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.5.tgz", + "integrity": "sha512-J2ngOB4cNzmqLHh6TYMM/ips8aoZIuzxJnDdWutBw5482jGXiOzsPoEF4j2WJ2mGnm7FBCO4StGcwzOgic70JQ==", + "dev": true, + "optional": true + }, + "esbuild-netbsd-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.5.tgz", + "integrity": "sha512-MmKUYGDizYjFia0Rwt8oOgmiFH7zaYlsoQ3tIOfPxOqLssAsEgG0MUdRDm5lliqjiuoog8LyDu9srQk5YwWF3w==", + "dev": true, + "optional": true + }, + "esbuild-openbsd-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.5.tgz", + "integrity": "sha512-2mMFfkLk3oPWfopA9Plj4hyhqHNuGyp5KQyTT9Rc8hFd8wAn5ZrbJg+gNcLMo2yzf8Uiu0RT6G9B15YN9WQyMA==", + "dev": true, + "optional": true + }, + "esbuild-sunos-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-sunos-64/-/esbuild-sunos-64-0.15.5.tgz", + "integrity": "sha512-2sIzhMUfLNoD+rdmV6AacilCHSxZIoGAU2oT7XmJ0lXcZWnCvCtObvO6D4puxX9YRE97GodciRGDLBaiC6x1SA==", + "dev": true, + "optional": true + }, + "esbuild-wasm": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-wasm/-/esbuild-wasm-0.15.5.tgz", + "integrity": "sha512-lTJOEKekN/4JI/eOEq0wLcx53co2N6vaT/XjBz46D1tvIVoUEyM0o2K6txW6gEotf31szFD/J1PbxmnbkGlK9A==", + "dev": true + }, + "esbuild-windows-32": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-windows-32/-/esbuild-windows-32-0.15.5.tgz", + "integrity": "sha512-e+duNED9UBop7Vnlap6XKedA/53lIi12xv2ebeNS4gFmu7aKyTrok7DPIZyU5w/ftHD4MUDs5PJUkQPP9xJRzg==", + "dev": true, + "optional": true + }, + "esbuild-windows-64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-windows-64/-/esbuild-windows-64-0.15.5.tgz", + "integrity": "sha512-v+PjvNtSASHOjPDMIai9Yi+aP+Vwox+3WVdg2JB8N9aivJ7lyhp4NVU+J0MV2OkWFPnVO8AE/7xH+72ibUUEnw==", + "dev": true, + "optional": true + }, + "esbuild-windows-arm64": { + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.5.tgz", + "integrity": "sha512-Yz8w/D8CUPYstvVQujByu6mlf48lKmXkq6bkeSZZxTA626efQOJb26aDGLzmFWx6eg/FwrXgt6SZs9V8Pwy/aA==", + "dev": true, + "optional": true + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "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": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==" + }, + "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": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "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": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "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": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + } + } + }, + "eslint": { + "version": "8.42.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.42.0.tgz", + "integrity": "sha512-ulg9Ms6E1WPf67PHaEY4/6E2tEn5/f7FXGzr3t9cBMugOmf1INYvuUwwh1aXQN4MfJ6a5K2iNwP3w4AColvI9A==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.4.0", + "@eslint/eslintrc": "^2.0.3", + "@eslint/js": "8.42.0", + "@humanwhocodes/config-array": "^0.11.10", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "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.2.0", + "eslint-visitor-keys": "^3.4.1", + "espree": "^9.5.2", + "esquery": "^1.4.2", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.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" + } + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "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" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.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==", + "dev": true, + "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==", + "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==", + "dev": true + }, + "eslint-scope": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.0.tgz", + "integrity": "sha512-DYj5deGlHBfMt15J7rdtyKNq/Nqlv5KfU4iodrQ019XESsRnwXH9KAE0y3cwtUHDo2ob7CypAnCqefh6vioWRw==", + "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 + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "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.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "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==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "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": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "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" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "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.8.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.8.0.tgz", + "integrity": "sha512-wLbQiFre3tdGgpDv67NQKnJuTlcUVYHas3k+DZCc2U2BadthoEY4B7hLPvAxaqdyOGCzuLfii2fqGph10va7oA==", + "dev": true, + "requires": {} + }, + "eslint-plugin-cypress": { + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-cypress/-/eslint-plugin-cypress-2.12.1.tgz", + "integrity": "sha512-c2W/uPADl5kospNDihgiLc7n87t5XhUbFDoTl6CfVkmG+kDAb5Ux10V9PoLPu9N+r7znpc+iQlcmAqT1A/89HA==", + "dev": true, + "requires": { + "globals": "^11.12.0" + } + }, + "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.4.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz", + "integrity": "sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA==", + "dev": true + }, + "eslint-webpack-plugin": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/eslint-webpack-plugin/-/eslint-webpack-plugin-3.2.0.tgz", + "integrity": "sha512-avrKcGncpPbPSUHX6B3stNGzkKFto3eL+DKM4+VyMrVnhPc3vRczVlCq3uhuFOdRvDHTVXuzwk1ZKUrqDQHQ9w==", + "dev": true, + "requires": { + "@types/eslint": "^7.29.0 || ^8.4.1", + "jest-worker": "^28.0.2", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "schema-utils": "^4.0.0" + }, + "dependencies": { + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } + } + } + }, + "espree": { + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.5.2.tgz", + "integrity": "sha512-7OASN1Wma5fum5SrNhFMAMJxOUAbhyfQ8dQ//PJaJbNw0URTPWqIghHWt1MmAANKhHZIYOHruW4Kw4ruUWOdGw==", + "dev": true, + "requires": { + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + } + }, + "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.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", + "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": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true + }, + "eventemitter-asyncresource": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/eventemitter-asyncresource/-/eventemitter-asyncresource-1.0.0.tgz", + "integrity": "sha512-39F7TBIV0G7gTelxwbEqnwhp90eqCPON1k0NwNfwhgKn4Co4ybUbj2pECcXT0B3ztRKZ7Pw1JujUUgmQJHcVAQ==", + "dev": true + }, + "eventemitter2": { + "version": "6.4.7", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.7.tgz", + "integrity": "sha512-tYUSVOGeQPKt/eC1ABfhHy5Xd96N3oIijJvN3O9+TsC28T5V9yX9oEfEK5faP0EFSNVOG97qtAS68GBrQB2hDg==", + "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": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==", + "dev": true + }, + "execa": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", + "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.0", + "get-stream": "^5.0.0", + "human-signals": "^1.1.1", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.0", + "onetime": "^5.1.0", + "signal-exit": "^3.0.2", + "strip-final-newline": "^2.0.0" + } + }, + "executable": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/executable/-/executable-4.1.1.tgz", + "integrity": "sha512-8iA79xD3uAch729dUG8xaaBBFGaEa0wdD2VkYLFHwlqosEj/jT66AzcreRDSgV7ehnNLBW2WR5jIXwGKjVdTLg==", + "dev": true, + "requires": { + "pify": "^2.2.0" + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", + "dev": true + }, + "expect": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/expect/-/expect-28.1.3.tgz", + "integrity": "sha512-eEh0xn8HlsuOBxFgIss+2mX85VAS4Qy3OSkjV7rlBWljtA4oWH37glVGyOZSZvErDT/yBywZdPGwCXuTvSG85g==", + "dev": true, + "requires": { + "@jest/expect-utils": "^28.1.3", + "jest-get-type": "^28.0.2", + "jest-matcher-utils": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-util": "^28.1.3" + } + }, + "express": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.18.1.tgz", + "integrity": "sha512-zZBcOX9TfehHQhtupq57OF8lFZ3UZi08Y97dwFCkD8p9d/d2Y3M+ykKcwaMDEL+4qyUolgBDX6AblpR3fL212Q==", + "dev": true, + "requires": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.0", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.5.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.2.0", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.7", + "qs": "6.10.3", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.18.0", + "serve-static": "1.15.0", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "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": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "dev": true + }, + "cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "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" + } + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true + }, + "destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "dev": true + }, + "finalhandler": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.2.0.tgz", + "integrity": "sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + }, + "on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "requires": { + "ee-first": "1.1.1" + } + }, + "qs": { + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.10.3.tgz", + "integrity": "sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==", + "dev": true, + "requires": { + "side-channel": "^1.0.4" + } + }, + "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 + }, + "send": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.18.0.tgz", + "integrity": "sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==", + "dev": true, + "requires": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "dependencies": { + "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 + } + } + }, + "serve-static": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz", + "integrity": "sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==", + "dev": true, + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.18.0" + } + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true + } + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true + }, + "external-editor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz", + "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==", + "dev": true, + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + }, + "dependencies": { + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + } + } + }, + "extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "requires": { + "@types/yauzl": "^2.9.1", + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + } + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", + "dev": true + }, + "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==", + "dev": true + }, + "fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "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": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "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": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "requires": { + "pend": "~1.2.0" + } + }, + "fecha": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", + "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", + "dev": true + }, + "figures": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", + "integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "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" + } + }, + "filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dev": true, + "requires": { + "minimatch": "^5.0.1" + } + }, + "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==", + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "finalhandler": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz", + "integrity": "sha512-ejnvM9ZXYzp6PUPUyQBMBf0Co5VX2gr5H2VQe2Ui2jWXNlxv+PYZo8wpAymJNJdLsG1R4p+M4aynF8KuoUEwRw==", + "dev": true, + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.1", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.2", + "statuses": "~1.3.1", + "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": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + } + } + }, + "find-cache-dir": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz", + "integrity": "sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^3.0.2", + "pkg-dir": "^4.1.0" + } + }, + "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" + } + }, + "find-yarn-workspace-root2": { + "version": "1.2.16", + "resolved": "https://registry.npmjs.org/find-yarn-workspace-root2/-/find-yarn-workspace-root2-1.2.16.tgz", + "integrity": "sha512-hr6hb1w8ePMpPVUK39S4RlwJzi+xPLuVuG8XlwXU3KD5Yn3qgBWVfy3AzNlDhWvE1EORCE65/Qm26rFQt3VLVA==", + "dev": true, + "requires": { + "micromatch": "^4.0.2", + "pkg-dir": "^4.2.0" + } + }, + "first-chunk-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/first-chunk-stream/-/first-chunk-stream-2.0.0.tgz", + "integrity": "sha512-X8Z+b/0L4lToKYq+lwnKqi9X/Zek0NibLpsJgVsSxpoYq7JtiCtRb5HqKVEjEw/qAb/4AKKRLOwwKHlWNpm2Eg==", + "dev": true, + "requires": { + "readable-stream": "^2.0.2" + }, + "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" + } + } + } + }, + "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.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "dev": true + }, + "fn.name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", + "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", + "dev": true + }, + "folder-hash": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/folder-hash/-/folder-hash-4.0.2.tgz", + "integrity": "sha512-Iw9GCqdA+zHfDVvk90TSAV66jq0IwiZaPvPgUiW+DHRwnaPOeZomzlgutx9QclinsQGz/XcVIGlDEJbFhCV5wA==", + "dev": true, + "requires": { + "debug": "^4.3.3", + "graceful-fs": "~4.2.9", + "minimatch": "~5.0.0" + }, + "dependencies": { + "minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, + "follow-redirects": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", + "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==", + "dev": true + }, + "for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "requires": { + "is-callable": "^1.1.3" + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", + "dev": true + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "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 + }, + "fraction.js": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz", + "integrity": "sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==", + "dev": true + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "dev": true + }, + "fs-extra": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-3.0.1.tgz", + "integrity": "sha512-V3Z3WZWVUYd8hoCL5xfXJCaHWYzmtwW5XWYSlLgERi8PWd8bx1kUHUk8L1BT57e49oKnDDD180mjfrHc1yA9rg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^3.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": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "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 + }, + "function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + } + }, + "functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true + }, + "gauge": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", + "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "dev": true, + "requires": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + } + }, + "generator-jhipster": { + "version": "7.9.3", + "resolved": "https://registry.npmjs.org/generator-jhipster/-/generator-jhipster-7.9.3.tgz", + "integrity": "sha512-iuhXUA0jHygv9wg3cCK+1xdNchi8x3z56TtTPqIeiDP0VNHL2ddesicL4QwNnHzMafk+J1po7hDEGQqURDUpvA==", + "dev": true, + "requires": { + "@faker-js/faker": "5.5.3", + "aws-sdk": "2.1204.0", + "axios": "0.27.2", + "chalk": "4.1.2", + "chevrotain": "10.3.0", + "commander": "9.4.0", + "conf": "10.2.0", + "debug": "4.3.4", + "didyoumean": "1.2.2", + "ejs": "3.1.8", + "escape-string-regexp": "4.0.0", + "glob": "8.0.3", + "insight": "0.11.1", + "isbinaryfile": "4.0.10", + "js-yaml": "4.1.0", + "lodash": "4.17.21", + "mem-fs-editor": "9.5.0", + "minimatch": "5.1.0", + "normalize-path": "3.0.0", + "os-locale": "5.0.0", + "p-queue": "6.6.2", + "p-transform": "1.3.0", + "parse-gitignore": "2.0.0", + "pluralize": "8.0.0", + "prettier": "2.7.1", + "prettier-plugin-java": "1.6.2", + "prettier-plugin-packagejson": "2.2.18", + "progress": "2.0.3", + "randexp": "0.5.3", + "semver": "7.3.7", + "shelljs": "0.8.5", + "simple-git": "3.13.0", + "then-request": "6.0.2", + "uuid": "8.3.2", + "winston": "3.8.1", + "yeoman-environment": "3.10.0", + "yeoman-generator": "5.7.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "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" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.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==", + "dev": true, + "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==", + "dev": true + }, + "commander": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.4.0.tgz", + "integrity": "sha512-sRPT+umqkz90UA8M1yqYfnHlZA7fF6nSphDtxeywPZ49ysjxDQybzk13CL+mXekDRG92skbcqCLVovuCusNmFw==", + "dev": true + }, + "detect-indent": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", + "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", + "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==", + "dev": true + }, + "git-hooks-list": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/git-hooks-list/-/git-hooks-list-1.0.3.tgz", + "integrity": "sha512-Y7wLWcrLUXwk2noSka166byGCvhMtDRpgHdzCno1UQv/n/Hegp++a2xBWJL1lJarnKD3SWaljD+0z1ztqxuKyQ==", + "dev": true + }, + "globby": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.0.tgz", + "integrity": "sha512-3LifW9M4joGZasyYPz2A1U74zbC/45fvpXUvO/9KbSa+VV0aGZarWkfdgKyR9sExNP0t0x0ss/UMJpNpcaTspw==", + "dev": true, + "requires": { + "@types/glob": "^7.1.1", + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.0.3", + "glob": "^7.1.3", + "ignore": "^5.1.1", + "merge2": "^1.2.3", + "slash": "^3.0.0" + }, + "dependencies": { + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "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" + } + } + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "java-parser": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/java-parser/-/java-parser-2.0.2.tgz", + "integrity": "sha512-fwv1eDYE4OIAN+XS7cD8aB7UdQyAh3Uz36ydWqemvnDKXEdLbxq7qIbvsjpSvS1NHFR+r81N7AjGcpnamjVxJw==", + "dev": true, + "requires": { + "chevrotain": "6.5.0", + "lodash": "4.17.21" + }, + "dependencies": { + "chevrotain": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-6.5.0.tgz", + "integrity": "sha512-BwqQ/AgmKJ8jcMEjaSnfMybnKMgGTrtDKowfTP3pX4jwVy0kNjRsT/AP6h+wC3+3NC+X8X15VWBnTCQlX+wQFg==", + "dev": true, + "requires": { + "regexp-to-ast": "0.4.0" + } + } + } + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "prettier": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz", + "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==", + "dev": true + }, + "prettier-plugin-java": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/prettier-plugin-java/-/prettier-plugin-java-1.6.2.tgz", + "integrity": "sha512-oVIUOkx50eb9skdRtEIU7MJUsizQ1ZocgXR1w1o8AnieNGpuPI/2pWnpjtbBm9wUG1dHtBGU8cVIr8h+xgzQIg==", + "dev": true, + "requires": { + "java-parser": "2.0.2", + "lodash": "4.17.21", + "prettier": "2.3.1" + }, + "dependencies": { + "prettier": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.3.1.tgz", + "integrity": "sha512-p+vNbgpLjif/+D+DwAZAbndtRrR0md0MwfmOVN9N+2RgyACMT+7tfaRnT+WDPkqnuVwleyuBIG2XBxKDme3hPA==", + "dev": true + } + } + }, + "prettier-plugin-packagejson": { + "version": "2.2.18", + "resolved": "https://registry.npmjs.org/prettier-plugin-packagejson/-/prettier-plugin-packagejson-2.2.18.tgz", + "integrity": "sha512-iBjQ3IY6IayFrQHhXvg+YvKprPUUiIJ04Vr9+EbeQPfwGajznArIqrN33c5bi4JcIvmLHGROIMOm9aYakJj/CA==", + "dev": true, + "requires": { + "sort-package-json": "1.57.0" + } + }, + "regexp-to-ast": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/regexp-to-ast/-/regexp-to-ast-0.4.0.tgz", + "integrity": "sha512-4qf/7IsIKfSNHQXSwial1IFmfM1Cc/whNBQqRwe0V2stPe7KmN1U0tWQiIx6JiirgSrisjE0eECdNf7Tav1Ntw==", + "dev": true + }, + "sort-package-json": { + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/sort-package-json/-/sort-package-json-1.57.0.tgz", + "integrity": "sha512-FYsjYn2dHTRb41wqnv+uEqCUvBpK3jZcTp9rbz2qDTmel7Pmdtf+i2rLaaPMRZeSVM60V3Se31GyWFpmKs4Q5Q==", + "dev": true, + "requires": { + "detect-indent": "^6.0.0", + "detect-newline": "3.1.0", + "git-hooks-list": "1.0.3", + "globby": "10.0.0", + "is-plain-obj": "2.1.0", + "sort-object-keys": "^1.1.3" + } + }, + "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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "generator-jhipster-es-entity-reindexer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/generator-jhipster-es-entity-reindexer/-/generator-jhipster-es-entity-reindexer-1.0.4.tgz", + "integrity": "sha512-oPa26y8QC1622g0YfDMI/Vcqz5EfECzlHL68S7t8AnxplmLVzqPjyzjOPU0pbfjvSbM7kvlXdOfGT6mNXTJIcw==", + "dev": true, + "requires": { + "chalk": "4.1.0", + "generator-jhipster": ">=7.0.0", + "semver": "7.1.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", + "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.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==", + "dev": true, + "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==", + "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==", + "dev": true + }, + "semver": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.1.1.tgz", + "integrity": "sha512-WfuG+fl6eh3eZ2qAf6goB7nhiCd7NPXhmyFxigB/TOkQyeLP8w8GsVehvtGNtnNmyboz4TgeK40B1Kbql/8c5A==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "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==" + }, + "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==" + }, + "get-intrinsic": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.2.tgz", + "integrity": "sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA==", + "dev": true, + "requires": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + } + }, + "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": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "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" + } + }, + "getos": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/getos/-/getos-3.2.1.tgz", + "integrity": "sha512-U56CfOK17OKgTVqozZjUKNdkfEv6jk5WISBJ8SHoagjE6L69zOwl3Z+O8myjY9MEW3i2HPWQBt/LTbCgcC973Q==", + "dev": true, + "requires": { + "async": "^3.2.0" + } + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "dev": true, + "requires": { + "assert-plus": "^1.0.0" + } + }, + "git-hooks-list": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/git-hooks-list/-/git-hooks-list-3.1.0.tgz", + "integrity": "sha512-LF8VeHeR7v+wAbXqfgRlTSX/1BJR9Q1vEMR8JAz1cEg6GX07+zyj3sAdDvYjj/xnlIfVuGgj4qBei1K3hKH+PA==", + "dev": true + }, + "github-username": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/github-username/-/github-username-6.0.0.tgz", + "integrity": "sha512-7TTrRjxblSI5l6adk9zd+cV5d6i1OrJSo3Vr9xdGqFLBQo0mz5P9eIfKCDJ7eekVGGFLbce0qbPSnktXV2BjDQ==", + "dev": true, + "requires": { + "@octokit/rest": "^18.0.6" + } + }, + "glob": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", + "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.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==", + "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-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" + }, + "dependencies": { + "ini": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-2.0.0.tgz", + "integrity": "sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==", + "dev": true + } + } + }, + "globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" + }, + "globalyzer": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/globalyzer/-/globalyzer-0.1.0.tgz", + "integrity": "sha512-40oNTM9UfG6aBmuKxk/giHn5nQ8RVz/SS4Ir6zgzOv9/qC3kKZ9v4etGTcJbEl/NyVQH7FGU7d+X1egr57Md2Q==", + "dev": true + }, + "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" + } + }, + "globrex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", + "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==", + "dev": true + }, + "graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, + "graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, + "grouped-queue": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/grouped-queue/-/grouped-queue-2.0.0.tgz", + "integrity": "sha512-/PiFUa7WIsl48dUeCvhIHnwNmAAzlI/eHoJl0vu3nsFA366JleY7Ff8EVTplZu5kO0MIdZjKTTnzItL61ahbnw==", + "dev": true + }, + "growly": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", + "integrity": "sha512-+xGQY0YyAWCnqy7Cd++hc2JqMYzlm0dG30Jd0beaA64sROr8C4nt8Yc9V5Ro3avlSUDTN0ulqP/VBKi1/lLygw==", + "dev": true + }, + "gzip-size": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz", + "integrity": "sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==", + "dev": true, + "requires": { + "duplexer": "^0.1.2" + } + }, + "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 + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", + "dev": true + }, + "har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "dev": true, + "requires": { + "ajv": "^6.12.3", + "har-schema": "^2.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" + } + }, + "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 + } + } + }, + "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.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" + }, + "has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dev": true, + "requires": { + "get-intrinsic": "^1.1.1" + } + }, + "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-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "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 + }, + "hosted-git-info": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-5.1.0.tgz", + "integrity": "sha512-Ek+QmMEqZF8XrbFdwoDjSbm7rT23pCgEMOJmz6GPk/s4yH//RQfNPArhIxbguNxROq/+5lNBwCDHMhA903Kx1Q==", + "dev": true, + "requires": { + "lru-cache": "^7.5.1" + } + }, + "hpack.js": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", + "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", + "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": "3.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", + "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", + "dev": true, + "requires": { + "whatwg-encoding": "^2.0.0" + } + }, + "html-entities": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", + "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==", + "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-basic": { + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/http-basic/-/http-basic-8.1.3.tgz", + "integrity": "sha512-/EcDMwJZh3mABI2NhGfHOGOeOZITqfkEO4p/xK+l3NpyncIHUQBoMvCSF/b5GqvKtySC2srL/GGG3+EtlqlmCw==", + "dev": true, + "requires": { + "caseless": "^0.12.0", + "concat-stream": "^1.6.2", + "http-response-object": "^3.0.1", + "parse-cache-control": "^1.0.1" + } + }, + "http-cache-semantics": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", + "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", + "dev": true + }, + "http-deceiver": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", + "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", + "dev": true + }, + "http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dev": true, + "requires": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "dependencies": { + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true + } + } + }, + "http-parser-js": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", + "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": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "requires": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + } + }, + "http-proxy-middleware": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/http-proxy-middleware/-/http-proxy-middleware-2.0.6.tgz", + "integrity": "sha512-ya/UeJ6HVBYxrgYotAZo1KvPWlgB48kUJLDePFeneHsVujFaW5WNj2NgWCAE//B1Dl02BIfYlpNgBy8Kf8Rjmw==", + "dev": true, + "requires": { + "@types/http-proxy": "^1.17.8", + "http-proxy": "^1.18.1", + "is-glob": "^4.0.1", + "is-plain-obj": "^3.0.0", + "micromatch": "^4.0.2" + }, + "dependencies": { + "is-plain-obj": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-3.0.0.tgz", + "integrity": "sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==", + "dev": true + } + } + }, + "http-response-object": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/http-response-object/-/http-response-object-3.0.2.tgz", + "integrity": "sha512-bqX0XTF6fnXSQcEJ2Iuyr75yVakyjIDCqroJQ/aHfSdlM743Cwqoi2nDYMzLGWUcuTWGWy8AAvOKXTfiv6q9RA==", + "dev": true, + "requires": { + "@types/node": "^10.0.3" + }, + "dependencies": { + "@types/node": { + "version": "10.17.60", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz", + "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==", + "dev": true + } + } + }, + "http-signature": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.3.6.tgz", + "integrity": "sha512-3adrsD6zqo4GsTqtO7FyrejHNv+NgiIfAfv68+jVlFmSr9OGy7zrxONceFRLKvnnZA5jbxQBX1u9PpB6Wi32Gw==", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^2.0.2", + "sshpk": "^1.14.1" + } + }, + "https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "human-signals": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", + "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", + "dev": true + }, + "humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "dev": true, + "requires": { + "ms": "^2.0.0" + } + }, + "husky": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz", + "integrity": "sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==", + "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==", + "dev": true, + "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-walk": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-5.0.1.tgz", + "integrity": "sha512-yemi4pMf51WKT7khInJqAvsIGzoqYXblnsz0ql8tM+yi1EKYTY1evX4NAbJrLL/Aanr2HyZeluqU+Oi7MGHokw==", + "dev": true, + "requires": { + "minimatch": "^5.0.1" + } + }, + "image-size": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz", + "integrity": "sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==", + "dev": true, + "optional": true + }, + "immutable": { + "version": "3.8.2", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz", + "integrity": "sha512-15gZoQ38eYjEjxkorfbcgBKBL6R7T459OuK+CpcWt7O3KF4uPCx2tD0uFETlUDIyo+1789crbMhTvQBSR5yBMg==", + "dev": 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" + }, + "dependencies": { + "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 + } + } + }, + "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": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "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 + }, + "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": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "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": "3.0.0", + "resolved": "https://registry.npmjs.org/ini/-/ini-3.0.0.tgz", + "integrity": "sha512-TxYQaeNW/N8ymDvwAxPyRbhMBtnEwuvaTYpOQkFx1nSeusgezHniEc/l35Vo4iCq/mMiTJbpD7oYxN98hFlfmw==", + "dev": true + }, + "inquirer": { + "version": "8.2.4", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-8.2.4.tgz", + "integrity": "sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg==", + "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.4.1", + "run-async": "^2.4.0", + "rxjs": "^7.5.5", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "through": "^2.3.6", + "wrap-ansi": "^7.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.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==", + "dev": true, + "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==", + "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==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "insight": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/insight/-/insight-0.11.1.tgz", + "integrity": "sha512-TBcZ0qC9dgdmcxL93OoqkY/RZXJtIi0i07phX/QyYk2ysmJtZex59dgTj4Doq50N9CG9dLRe/RIudc/5CCoFNw==", + "dev": true, + "requires": { + "async": "^2.6.2", + "chalk": "^4.1.1", + "conf": "^10.0.1", + "inquirer": "^6.3.1", + "lodash.debounce": "^4.0.8", + "os-name": "^4.0.1", + "request": "^2.88.0", + "tough-cookie": "^4.0.0", + "uuid": "^8.3.2" + }, + "dependencies": { + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==", + "dev": true + }, + "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 + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "dev": true, + "requires": { + "lodash": "^4.17.14" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==", + "dev": true, + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "cli-width": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.1.tgz", + "integrity": "sha512-GRMWDxpOB6Dgk2E5Uo+3eEBvtOOlimMmpbFiKuLFnQzYDavtLFY3K5ona41jgN/WdRZtG7utuVSVTL4HbZHGkw==", + "dev": true + }, + "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==", + "dev": true, + "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==", + "dev": true + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "inquirer": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.5.2.tgz", + "integrity": "sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ==", + "dev": true, + "requires": { + "ansi-escapes": "^3.2.0", + "chalk": "^2.4.2", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.3", + "figures": "^2.0.0", + "lodash": "^4.17.12", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rxjs": "^6.4.0", + "string-width": "^2.1.0", + "strip-ansi": "^5.1.0", + "through": "^2.3.6" + }, + "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": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "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" + } + } + } + }, + "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": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==", + "dev": true + }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha512-r65nCZhrbXXb6dXOACihYApHw2Q6pV0M3V0PSxd74N0+D8nzAdEAITq2oAjA1jVnKI+tGvEBUpqiMh0+rW6zDQ==", + "dev": true + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha512-oyyPpiMaKARvvcgip+JV+7zci5L8D1W9RZIz2l1o08AM3pfspitVWnPt3mzHcBPp12oYMTy0pqrFs/C+m3EwsQ==", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha512-6IzJLuGi4+R14vwagDHX+JrXmPVtPpn4mffDJ1UdR7/Edm87fl6yi8mMBIVvFtJaNTUvjughmW4hwLhRG7gC1Q==", + "dev": true, + "requires": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + } + }, + "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" + } + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "dev": true + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + } + } + }, + "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" + } + }, + "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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "tough-cookie": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.2.tgz", + "integrity": "sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==", + "dev": true, + "requires": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "dev": true + } + } + }, + "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" + } + }, + "interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "dev": true + }, + "invert-kv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-3.0.1.tgz", + "integrity": "sha512-CYdFeFexxhv/Bcny+Q0BfOV+ltRlJcd4BBZBYFX/O0u4npJrgZtIcjokegtiSMAvlMTJ+Koq0GBCc//3bueQxw==", + "dev": true + }, + "ip": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ip/-/ip-2.0.0.tgz", + "integrity": "sha512-WKa+XuLG1A1R0UWhl2+1XQSi+fZWMsYKffMZTTYsiZaUD8k2yDAj5atimTUD2TZkyCkNEeYE5NhFZmupOGtjYQ==", + "dev": true + }, + "ipaddr.js": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.0.1.tgz", + "integrity": "sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==", + "dev": true + }, + "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": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "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==", + "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-callable": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.5.tgz", + "integrity": "sha512-ZIWRujF6MvYGkEuHMYtFRkL2wAtFw89EHfKlXrkPkjQZZRWeh9L1q3SV13NIfHnqxugjLvAOkEHx9mb1zcMnEw==", + "dev": true + }, + "is-ci": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", + "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", + "dev": true, + "requires": { + "ci-info": "^3.2.0" + } + }, + "is-core-module": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.10.0.tgz", + "integrity": "sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "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-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-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" + }, + "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-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dev": true, + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "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-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", + "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-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + }, + "is-number-like": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/is-number-like/-/is-number-like-1.0.8.tgz", + "integrity": "sha512-6rZi3ezCyFcn5L71ywzz2bS5b2Igl1En3eTlZlvKjpz1n3IZLAYMbKYAIQgFmEu0GENg92ziU/faEOA/aixjbA==", + "dev": true, + "requires": { + "lodash.isfinite": "^3.3.2" + } + }, + "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==", + "dev": true + }, + "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-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "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-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-scoped": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-scoped/-/is-scoped-2.1.0.tgz", + "integrity": "sha512-Cv4OpPTHAK9kHYzkzCrof3VJh7H/PrG2MBUMvvJebaaUMbqhm0YAtXnvh0I3Hnj2tMZWwrRROWLSgfJrKqWmlQ==", + "dev": true, + "requires": { + "scoped-regex": "^2.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-typed-array": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.9.tgz", + "integrity": "sha512-kfrlnTTn8pZkfpJMUgYD7YZ3qzeJgWUn8XfVYBARc4wnmNOmLbmuuaAs3q5fvB0UJOn6yHAKaGTPM7d6ezoD/A==", + "dev": true, + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-abstract": "^1.20.0", + "for-each": "^0.3.3", + "has-tostringtag": "^1.0.0" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==", + "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-utf8": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", + "integrity": "sha512-rMYPYvCzsXywIsldgLaSoPlw5PfoB/ssr7hY4pLfcodrA5M/eArza1a9VmTiNIBNMjOGr1Ow9mTyU2o69U6U9Q==", + "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-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" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "isbinaryfile": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", + "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "dev": true + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==", + "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": "5.2.0", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.0.tgz", + "integrity": "sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A==", + "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" + }, + "dependencies": { + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "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" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "has-flag": "^4.0.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.5", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.5.tgz", + "integrity": "sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==", + "dev": true, + "requires": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } + }, + "jake": { + "version": "10.8.5", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.5.tgz", + "integrity": "sha512-sVpxYeuAhWt0OTWITwT98oyV0GsXyMlXCF+3L1SuafBVUIr/uILGRB+NqwkzhgXKvoJpDIpQvqkUALgdmQsQxw==", + "dev": true, + "requires": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.1", + "minimatch": "^3.0.4" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.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" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.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==", + "dev": true, + "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==", + "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==", + "dev": true + }, + "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" + } + }, + "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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "java-parser": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/java-parser/-/java-parser-2.0.4.tgz", + "integrity": "sha512-8dzy+3lJdSakeXgYArzqJCTy2PuH4iW1WjbhPsDaiYDXPeeroksy0s1/TWoc9Zw8zWzS3x8soanHh0AThwwqrQ==", + "dev": true, + "requires": { + "chevrotain": "6.5.0", + "lodash": "4.17.21" + }, + "dependencies": { + "chevrotain": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/chevrotain/-/chevrotain-6.5.0.tgz", + "integrity": "sha512-BwqQ/AgmKJ8jcMEjaSnfMybnKMgGTrtDKowfTP3pX4jwVy0kNjRsT/AP6h+wC3+3NC+X8X15VWBnTCQlX+wQFg==", + "dev": true, + "requires": { + "regexp-to-ast": "0.4.0" + } + }, + "regexp-to-ast": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/regexp-to-ast/-/regexp-to-ast-0.4.0.tgz", + "integrity": "sha512-4qf/7IsIKfSNHQXSwial1IFmfM1Cc/whNBQqRwe0V2stPe7KmN1U0tWQiIx6JiirgSrisjE0eECdNf7Tav1Ntw==", + "dev": true + } + } + }, + "jest": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest/-/jest-28.1.3.tgz", + "integrity": "sha512-N4GT5on8UkZgH0O5LUavMRV1EDEhNTL0KEfRmDIeZHSV7p2XgLoY9t9VDUgL6o+yfdgYHVxuz81G8oB9VG5uyA==", + "dev": true, + "requires": { + "@jest/core": "^28.1.3", + "@jest/types": "^28.1.3", + "import-local": "^3.0.2", + "jest-cli": "^28.1.3" + } + }, + "jest-changed-files": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-28.1.3.tgz", + "integrity": "sha512-esaOfUWJXk2nfZt9SPyC8gA1kNfdKLkQWyzsMlqq8msYSlNKfmZxfRgZn4Cd4MGVUF+7v6dBs0d5TOAKa7iIiA==", + "dev": true, + "requires": { + "execa": "^5.0.0", + "p-limit": "^3.1.0" + }, + "dependencies": { + "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" + } + }, + "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 + }, + "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 + } + } + }, + "jest-circus": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-28.1.3.tgz", + "integrity": "sha512-cZ+eS5zc79MBwt+IhQhiEp0OeBddpc1n8MBo1nMB8A7oPMKEO+Sre+wHaLJexQUj9Ya/8NOBY0RESUgYjB6fow==", + "dev": true, + "requires": { + "@jest/environment": "^28.1.3", + "@jest/expect": "^28.1.3", + "@jest/test-result": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^0.7.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^28.1.3", + "jest-matcher-utils": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-runtime": "^28.1.3", + "jest-snapshot": "^28.1.3", + "jest-util": "^28.1.3", + "p-limit": "^3.1.0", + "pretty-format": "^28.1.3", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.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==", + "dev": true, + "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==", + "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==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-cli": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-28.1.3.tgz", + "integrity": "sha512-roY3kvrv57Azn1yPgdTebPAXvdR2xfezaKKYzVxZ6It/5NCxzJym6tUI5P1zkdWhfUYkxEI9uZWcQdaFLo8mJQ==", + "dev": true, + "requires": { + "@jest/core": "^28.1.3", + "@jest/test-result": "^28.1.3", + "@jest/types": "^28.1.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "import-local": "^3.0.2", + "jest-config": "^28.1.3", + "jest-util": "^28.1.3", + "jest-validate": "^28.1.3", + "prompts": "^2.0.1", + "yargs": "^17.3.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.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==", + "dev": true, + "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==", + "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==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-config": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-28.1.3.tgz", + "integrity": "sha512-MG3INjByJ0J4AsNBm7T3hsuxKQqFIiRo/AUqb1q9LRKI5UU6Aar9JHbr9Ivn1TVwfUD9KirRoM/T6u8XlcQPHQ==", + "dev": true, + "requires": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^28.1.3", + "@jest/types": "^28.1.3", + "babel-jest": "^28.1.3", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^28.1.3", + "jest-environment-node": "^28.1.3", + "jest-get-type": "^28.0.2", + "jest-regex-util": "^28.0.2", + "jest-resolve": "^28.1.3", + "jest-runner": "^28.1.3", + "jest-util": "^28.1.3", + "jest-validate": "^28.1.3", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^28.1.3", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.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" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.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==", + "dev": true, + "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==", + "dev": true + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "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" + } + }, + "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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-date-mock": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/jest-date-mock/-/jest-date-mock-1.0.8.tgz", + "integrity": "sha512-0Lyp+z9xvuNmLbK+5N6FOhSiBeux05Lp5bbveFBmYo40Aggl2wwxFoIrZ+rOWC8nDNcLeBoDd2miQdEDSf3iQw==", + "dev": true + }, + "jest-diff": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-28.1.3.tgz", + "integrity": "sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "diff-sequences": "^28.1.1", + "jest-get-type": "^28.0.2", + "pretty-format": "^28.1.3" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.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==", + "dev": true, + "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==", + "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==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-docblock": { + "version": "28.1.1", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-28.1.1.tgz", + "integrity": "sha512-3wayBVNiOYx0cwAbl9rwm5kKFP8yHH3d/fkEaL02NPTkDojPtheGB7HZSFY4wzX+DxyrvhXz0KSCVksmCknCuA==", + "dev": true, + "requires": { + "detect-newline": "^3.0.0" + } + }, + "jest-each": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-28.1.3.tgz", + "integrity": "sha512-arT1z4sg2yABU5uogObVPvSlSMQlDA48owx07BDPAiasW0yYpYHYOo4HHLz9q0BVzDVU4hILFjzJw0So9aCL/g==", + "dev": true, + "requires": { + "@jest/types": "^28.1.3", + "chalk": "^4.0.0", + "jest-get-type": "^28.0.2", + "jest-util": "^28.1.3", + "pretty-format": "^28.1.3" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.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==", + "dev": true, + "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==", + "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==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-environment-jsdom": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-28.1.3.tgz", + "integrity": "sha512-HnlGUmZRdxfCByd3GM2F100DgQOajUBzEitjGqIREcb45kGjZvRrKUdlaF6escXBdcXNl0OBh+1ZrfeZT3GnAg==", + "dev": true, + "requires": { + "@jest/environment": "^28.1.3", + "@jest/fake-timers": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/jsdom": "^16.2.4", + "@types/node": "*", + "jest-mock": "^28.1.3", + "jest-util": "^28.1.3", + "jsdom": "^19.0.0" + } + }, + "jest-environment-node": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-28.1.3.tgz", + "integrity": "sha512-ugP6XOhEpjAEhGYvp5Xj989ns5cB1K6ZdjBYuS30umT4CQEETaxSiPcZ/E1kFktX4GkrcM4qu07IIlDYX1gp+A==", + "dev": true, + "requires": { + "@jest/environment": "^28.1.3", + "@jest/fake-timers": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "jest-mock": "^28.1.3", + "jest-util": "^28.1.3" + } + }, + "jest-get-type": { + "version": "28.0.2", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-28.0.2.tgz", + "integrity": "sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==", + "dev": true + }, + "jest-haste-map": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-28.1.3.tgz", + "integrity": "sha512-3S+RQWDXccXDKSWnkHa/dPwt+2qwA8CJzR61w3FoYCvoo3Pn8tvGcysmMF0Bj0EX5RYvAI2EIvC57OmotfdtKA==", + "dev": true, + "requires": { + "@jest/types": "^28.1.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "fsevents": "^2.3.2", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^28.0.2", + "jest-util": "^28.1.3", + "jest-worker": "^28.1.3", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + } + }, + "jest-junit": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/jest-junit/-/jest-junit-14.0.1.tgz", + "integrity": "sha512-h7/wwzPbllgpQhhVcRzRC76/cc89GlazThoV1fDxcALkf26IIlRsu/AcTG64f4nR2WPE3Cbd+i/sVf+NCUHrWQ==", + "dev": true, + "requires": { + "mkdirp": "^1.0.4", + "strip-ansi": "^6.0.1", + "uuid": "^8.3.2", + "xml": "^1.0.1" + } + }, + "jest-leak-detector": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-28.1.3.tgz", + "integrity": "sha512-WFVJhnQsiKtDEo5lG2mM0v40QWnBM+zMdHHyJs8AWZ7J0QZJS59MsyKeJHWhpBZBH32S48FOVvGyOFT1h0DlqA==", + "dev": true, + "requires": { + "jest-get-type": "^28.0.2", + "pretty-format": "^28.1.3" + } + }, + "jest-matcher-utils": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz", + "integrity": "sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "jest-diff": "^28.1.3", + "jest-get-type": "^28.0.2", + "pretty-format": "^28.1.3" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.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==", + "dev": true, + "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==", + "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==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-message-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz", + "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^28.1.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^28.1.3", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.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==", + "dev": true, + "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==", + "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==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-mock": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-28.1.3.tgz", + "integrity": "sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA==", + "dev": true, + "requires": { + "@jest/types": "^28.1.3", + "@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": "12.2.2", + "resolved": "https://registry.npmjs.org/jest-preset-angular/-/jest-preset-angular-12.2.2.tgz", + "integrity": "sha512-aj5ZwVW6cGGzZKUn6e/jDwFgQh6FHy1zCCXWOeqFCuM3WODrbdUJ93zKrex18e9K1+PvOcP0e20yKbj3gwhfFg==", + "dev": true, + "requires": { + "bs-logger": "^0.2.6", + "esbuild": ">=0.13.8", + "esbuild-wasm": ">=0.13.8", + "jest-environment-jsdom": "^28.0.0", + "pretty-format": "^28.0.0", + "ts-jest": "^28.0.0" + } + }, + "jest-regex-util": { + "version": "28.0.2", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz", + "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==", + "dev": true + }, + "jest-resolve": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-28.1.3.tgz", + "integrity": "sha512-Z1W3tTjE6QaNI90qo/BJpfnvpxtaFTFw5CDgwpyE/Kz8U/06N1Hjf4ia9quUhCh39qIGWF1ZuxFiBiJQwSEYKQ==", + "dev": true, + "requires": { + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^28.1.3", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^28.1.3", + "jest-validate": "^28.1.3", + "resolve": "^1.20.0", + "resolve.exports": "^1.1.0", + "slash": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.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==", + "dev": true, + "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==", + "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==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-resolve-dependencies": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-28.1.3.tgz", + "integrity": "sha512-qa0QO2Q0XzQoNPouMbCc7Bvtsem8eQgVPNkwn9LnS+R2n8DaVDPL/U1gngC0LTl1RYXJU0uJa2BMC2DbTfFrHA==", + "dev": true, + "requires": { + "jest-regex-util": "^28.0.2", + "jest-snapshot": "^28.1.3" + } + }, + "jest-runner": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-28.1.3.tgz", + "integrity": "sha512-GkMw4D/0USd62OVO0oEgjn23TM+YJa2U2Wu5zz9xsQB1MxWKDOlrnykPxnMsN0tnJllfLPinHTka61u0QhaxBA==", + "dev": true, + "requires": { + "@jest/console": "^28.1.3", + "@jest/environment": "^28.1.3", + "@jest/test-result": "^28.1.3", + "@jest/transform": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.10.2", + "graceful-fs": "^4.2.9", + "jest-docblock": "^28.1.1", + "jest-environment-node": "^28.1.3", + "jest-haste-map": "^28.1.3", + "jest-leak-detector": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-resolve": "^28.1.3", + "jest-runtime": "^28.1.3", + "jest-util": "^28.1.3", + "jest-watcher": "^28.1.3", + "jest-worker": "^28.1.3", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.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==", + "dev": true, + "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==", + "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==", + "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 + }, + "source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-runtime": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-28.1.3.tgz", + "integrity": "sha512-NU+881ScBQQLc1JHG5eJGU7Ui3kLKrmwCPPtYsJtBykixrM2OhVQlpMmFWJjMyDfdkGgBMNjXCGB/ebzsgNGQw==", + "dev": true, + "requires": { + "@jest/environment": "^28.1.3", + "@jest/fake-timers": "^28.1.3", + "@jest/globals": "^28.1.3", + "@jest/source-map": "^28.1.2", + "@jest/test-result": "^28.1.3", + "@jest/transform": "^28.1.3", + "@jest/types": "^28.1.3", + "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": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-mock": "^28.1.3", + "jest-regex-util": "^28.0.2", + "jest-resolve": "^28.1.3", + "jest-snapshot": "^28.1.3", + "jest-util": "^28.1.3", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.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" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.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==", + "dev": true, + "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==", + "dev": true + }, + "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" + } + }, + "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 + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "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 + }, + "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" + } + }, + "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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-snapshot": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-28.1.3.tgz", + "integrity": "sha512-4lzMgtiNlc3DU/8lZfmqxN3AYD6GGLbl+72rdBpXvcV+whX7mDrREzkPdp2RnmfIiWBg1YbuFSkXduF2JcafJg==", + "dev": true, + "requires": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^28.1.3", + "@jest/transform": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/babel__traverse": "^7.0.6", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^28.1.3", + "graceful-fs": "^4.2.9", + "jest-diff": "^28.1.3", + "jest-get-type": "^28.0.2", + "jest-haste-map": "^28.1.3", + "jest-matcher-utils": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-util": "^28.1.3", + "natural-compare": "^1.4.0", + "pretty-format": "^28.1.3", + "semver": "^7.3.5" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.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==", + "dev": true, + "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==", + "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==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-sonar": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/jest-sonar/-/jest-sonar-0.2.12.tgz", + "integrity": "sha512-8hZO3rhxVobJkMhyuEgsZxfj0QQucCa+dkXVJ3ckz+Gi180F2ET12GwEGP/j4z1owc08Z2JgIt2Qlgl06+3asQ==", + "dev": true, + "requires": { + "entities": "^2.1.0", + "strip-ansi": "^6.0.0" + } + }, + "jest-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", + "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", + "dev": true, + "requires": { + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.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==", + "dev": true, + "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==", + "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==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-validate": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-28.1.3.tgz", + "integrity": "sha512-SZbOGBWEsaTxBGCOpsRWlXlvNkvTkY0XxRfh7zYmvd8uL5Qzyg0CHAXiXKROflh801quA6+/DsT4ODDthOC/OA==", + "dev": true, + "requires": { + "@jest/types": "^28.1.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^28.0.2", + "leven": "^3.1.0", + "pretty-format": "^28.1.3" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.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==", + "dev": true, + "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==", + "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==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-watcher": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-28.1.3.tgz", + "integrity": "sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g==", + "dev": true, + "requires": { + "@jest/test-result": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.10.2", + "jest-util": "^28.1.3", + "string-length": "^4.0.1" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.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==", + "dev": true, + "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==", + "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==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "jest-worker": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.3.tgz", + "integrity": "sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==", + "dev": true, + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "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" + } + } + } + }, + "jmespath": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", + "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==", + "dev": true + }, + "joi": { + "version": "17.9.2", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.9.2.tgz", + "integrity": "sha512-Itk/r+V4Dx0V3c7RLFdRh12IOjySm2/WGPMubBT92cQvRfYZhPM2W0hZlctjj72iES8jsRCwp7S/cRmWBnJ4nw==", + "dev": true, + "requires": { + "@hapi/hoek": "^9.0.0", + "@hapi/topo": "^5.0.0", + "@sideway/address": "^4.1.3", + "@sideway/formula": "^3.0.1", + "@sideway/pinpoint": "^2.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==" + }, + "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" + } + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", + "dev": true + }, + "jsdom": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-19.0.0.tgz", + "integrity": "sha512-RYAyjCbxy/vri/CfnjUWJQQtZ3LKlLnDqj+9XLNnJPgEGeirZs3hllKR20re8LUZ6o1b1X4Jat+Qd26zmP41+A==", + "dev": true, + "requires": { + "abab": "^2.0.5", + "acorn": "^8.5.0", + "acorn-globals": "^6.0.0", + "cssom": "^0.5.0", + "cssstyle": "^2.3.0", + "data-urls": "^3.0.1", + "decimal.js": "^10.3.1", + "domexception": "^4.0.0", + "escodegen": "^2.0.0", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy-agent": "^5.0.0", + "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": "^3.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^2.0.0", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^10.0.0", + "ws": "^8.2.3", + "xml-name-validator": "^4.0.0" + }, + "dependencies": { + "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==", + "dev": true, + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "tough-cookie": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.2.tgz", + "integrity": "sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==", + "dev": true, + "requires": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + } + }, + "universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "dev": true + } + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==" + }, + "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": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "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==", + "dev": true + }, + "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==", + "dev": true + }, + "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": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "json-stringify-nice": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/json-stringify-nice/-/json-stringify-nice-1.1.4.tgz", + "integrity": "sha512-5Z5RFW63yxReJ7vANgW6eZFGWaQvnPE3WNmZoOJrSkGju2etKA2L5rrOa1sm877TVTFt57A80BH1bArcmlLfPw==", + "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": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true + }, + "json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==" + }, + "jsonc-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.1.0.tgz", + "integrity": "sha512-DRf0QjnNeCUds3xTjKlQQ3DpJD51GvDjJfnxUVWg6PZTo2otSm+slzNAxU/35hF8/oJIKoG9slq30JYOsF2azg==", + "dev": true + }, + "jsonfile": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-3.0.1.tgz", + "integrity": "sha512-oBko6ZHlubVB5mRFkur5vgYR1UyqX+S6Y/oCfLhqNdcc2fYFlDpIoNc7AfKS1KOGcnNAkvsr0grLck9ANM815w==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "jsonparse": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", + "integrity": "sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==", + "dev": true + }, + "jsprim": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-2.0.2.tgz", + "integrity": "sha512-gqXddjPqQ6G40VdnI6T6yObEC+pDNvyP95wdQhkWkg7crHH3km5qP1FsOXEkzEQwnz6gz5qGTn1c2Y52wP3OyQ==", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + } + }, + "just-diff": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/just-diff/-/just-diff-5.1.1.tgz", + "integrity": "sha512-u8HXJ3HlNrTzY7zrYYKjNEfBlyjqhdBkoyTVdjtn7p02RJD5NvR8rIClzeGA7t+UYP1/7eAkWNLU0+P3QrEqKQ==", + "dev": true + }, + "just-diff-apply": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/just-diff-apply/-/just-diff-apply-5.4.1.tgz", + "integrity": "sha512-AAV5Jw7tsniWwih8Ly3fXxEZ06y+6p5TwQMsw0dzZ/wPKilzyDgdAnL0Ug4NNIquPUOh1vfFWEHbmXUqM5+o8g==", + "dev": true + }, + "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" + } + }, + "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 + }, + "kuler": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", + "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", + "dev": true + }, + "lazy-ass": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/lazy-ass/-/lazy-ass-1.6.0.tgz", + "integrity": "sha512-cc8oEVoctTvsFZ/Oje/kGnHbpWHYBe8IAJe4C0QNc3t8uM/0Y8+erSz/7Y1ALuXTEZTMvxXwO6YbX1ey3ujiZw==", + "dev": true + }, + "lcid": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-3.1.1.tgz", + "integrity": "sha512-M6T051+5QCGLBQb8id3hdvIW8+zeFV2FyBGFS9IEK5H9Wt4MueD4bW1eWikpHgZp+5xR3l5c8pZUkQsIA0BFZg==", + "dev": true, + "requires": { + "invert-kv": "^3.0.0" + } + }, + "less": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/less/-/less-4.1.3.tgz", + "integrity": "sha512-w16Xk/Ta9Hhyei0Gpz9m7VS8F28nieJaL/VyShID7cYvP6IL5oHeL6p4TXSDJqZE/lNv0oJ2pGVjJsRkfwm5FA==", + "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": "^3.1.0", + "parse-node-version": "^1.0.1", + "source-map": "~0.6.0", + "tslib": "^2.3.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.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "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 + } + } + }, + "less-loader": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/less-loader/-/less-loader-11.0.0.tgz", + "integrity": "sha512-9+LOWWjuoectIEx3zrfN83NAGxSUB5pWEabbbidVQVgZhN+wN68pOvuyirVlH1IK4VT1f3TmlyvAnCXh8O5KEw==", + "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": "4.0.2", + "resolved": "https://registry.npmjs.org/license-webpack-plugin/-/license-webpack-plugin-4.0.2.tgz", + "integrity": "sha512-771TFWFD70G1wLTC4oU2Cw4qvtmNrIw+wRvBtn+okgHl7slJVi7zfNcdmqDL72BojM30VNJ2UHylr1o77U37Jw==", + "dev": true, + "requires": { + "webpack-sources": "^3.0.0" + } + }, + "lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "dev": true + }, + "limiter": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz", + "integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==", + "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": "13.2.2", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-13.2.2.tgz", + "integrity": "sha512-71gSwXKy649VrSU09s10uAT0rWCcY3aewhMaHyl2N84oBk4Xs9HgxvUp3AYu+bNsK4NrOYYxvSgg7FyGJ+jGcA==", + "dev": true, + "requires": { + "chalk": "5.2.0", + "cli-truncate": "^3.1.0", + "commander": "^10.0.0", + "debug": "^4.3.4", + "execa": "^7.0.0", + "lilconfig": "2.1.0", + "listr2": "^5.0.7", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-inspect": "^1.12.3", + "pidtree": "^0.6.0", + "string-argv": "^0.3.1", + "yaml": "^2.2.2" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz", + "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==", + "dev": true + }, + "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==", + "dev": true, + "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==", + "dev": true + }, + "commander": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true + }, + "execa": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-7.1.1.tgz", + "integrity": "sha512-wH0eMf/UXckdUYnO21+HDztteVv05rq2GXksxT4fCGeHkBhw1DROXh40wcjMcRqDOWE7iPJ4n3M7e2+YFP+76Q==", + "dev": true, + "requires": { + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.1", + "human-signals": "^4.3.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^3.0.7", + "strip-final-newline": "^3.0.0" + } + }, + "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 + }, + "human-signals": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz", + "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==", + "dev": true + }, + "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 + }, + "is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dev": true + }, + "listr2": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-5.0.8.tgz", + "integrity": "sha512-mC73LitKHj9w6v30nLNGPetZIlfpUniNSsxxrbaPcWOjDb92SHPzJPi/t+v1YC/lxKz/AJ9egOjww0qUuFxBpA==", + "dev": true, + "requires": { + "cli-truncate": "^2.1.0", + "colorette": "^2.0.19", + "log-update": "^4.0.0", + "p-map": "^4.0.0", + "rfdc": "^1.3.0", + "rxjs": "^7.8.0", + "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" + } + } + } + }, + "mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dev": true + }, + "npm-run-path": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", + "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==", + "dev": true, + "requires": { + "path-key": "^4.0.0" + } + }, + "onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dev": true, + "requires": { + "mimic-fn": "^4.0.0" + } + }, + "path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dev": true + }, + "rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "requires": { + "tslib": "^2.1.0" + } + }, + "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" + } + }, + "strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true + } + } + }, + "listr2": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-3.14.0.tgz", + "integrity": "sha512-TyWI8G99GX9GjE54cJ+RrNMcIFBfwMPxc3XTFiAYGN4s10hWROGtOg7+O6u6LE3mNkyld7RSLE6nrKBvTfcs3g==", + "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.1", + "through": "^2.3.8", + "wrap-ansi": "^7.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "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" + } + }, + "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==", + "dev": true, + "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==", + "dev": true + }, + "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" + } + } + } + }, + "load-yaml-file": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/load-yaml-file/-/load-yaml-file-0.2.0.tgz", + "integrity": "sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.5", + "js-yaml": "^3.13.0", + "pify": "^4.0.1", + "strip-bom": "^3.0.0" + }, + "dependencies": { + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true + }, + "strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true + } + } + }, + "loader-runner": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.0.tgz", + "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", + "dev": true + }, + "loader-utils": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-3.2.0.tgz", + "integrity": "sha512-HVl9ZqccQihZ7JM85dco1MvO9G+ONvxoGa9rkhzFsneGLKSUg1gJf9bWzhRhcvm2qChhWpebQhP44qxjKIUCaQ==", + "dev": true + }, + "localtunnel": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/localtunnel/-/localtunnel-2.0.2.tgz", + "integrity": "sha512-n418Cn5ynvJd7m/N1d9WVJISLJF/ellZnfsLnx8WBWGzxv/ntNcFkJ1o6se5quUhCplfLGBNL5tYHiq5WF3Nug==", + "dev": true, + "requires": { + "axios": "0.21.4", + "debug": "4.3.2", + "openurl": "1.1.1", + "yargs": "17.1.1" + }, + "dependencies": { + "axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==", + "dev": true, + "requires": { + "follow-redirects": "^1.14.0" + } + }, + "debug": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", + "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "yargs": { + "version": "17.1.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.1.1.tgz", + "integrity": "sha512-c2k48R0PwKIqKhPMWjeiF6y2xY/gPMUlro0sgxqXpbOIohWiLNXWslsootttv7E1e73QPAMQSg5FeySbVcpsPQ==", + "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" + } + }, + "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 + } + } + }, + "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" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", + "dev": true + }, + "lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + }, + "lodash.debounce": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", + "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", + "dev": true + }, + "lodash.isfinite": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/lodash.isfinite/-/lodash.isfinite-3.3.2.tgz", + "integrity": "sha512-7FGG40uhC8Mm633uKW1r58aElFlBlxCrg9JfSi3P6aYiWmfiWF0PgMd86ZUsxE5GwWPdHoS2+48bwTh2VPkIQA==", + "dev": true + }, + "lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "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.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "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" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.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==", + "dev": true, + "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==", + "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==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "has-flag": "^4.0.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": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "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==", + "dev": true, + "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==", + "dev": true + }, + "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" + } + } + } + }, + "logform": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.4.2.tgz", + "integrity": "sha512-W4c9himeAwXEdZ05dQNerhFz2XG80P9Oj0loPUMV23VC2it0orMHQhJm4hdnnor3rd1HsGf6a2lPwBM1zeXHGw==", + "dev": true, + "requires": { + "@colors/colors": "1.5.0", + "fecha": "^4.2.0", + "ms": "^2.1.1", + "safe-stable-stringify": "^2.3.1", + "triple-beam": "^1.3.0" + } + }, + "lru-cache": { + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.14.0.tgz", + "integrity": "sha512-EIRtP1GrSJny0dqb50QXRUNBxHJhcpxHC++M5tD7RYbvLLn5KVWKsbyswSSqDuU15UFi3bgTQIY8nhDMeF6aDQ==", + "dev": true + }, + "macos-release": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/macos-release/-/macos-release-2.5.0.tgz", + "integrity": "sha512-EIgv+QZ9r+814gjJj0Bt5vSLJLzswGmSUbUpbi9AIr/fsN2IWFBl2NucV9PAiek+U1STK468tEkxmVYUtuAN3g==", + "dev": true + }, + "magic-string": { + "version": "0.26.2", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.26.2.tgz", + "integrity": "sha512-NzzlXpclt5zAbmo6h6jNc8zl2gNRGHvmsZW4IvZhTC4W7k4OlLP+S5YLussa/r3ixNT66KOQfNORlXHSOy/X4A==", + "requires": { + "sourcemap-codec": "^1.4.8" + } + }, + "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.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "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 + }, + "make-fetch-happen": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz", + "integrity": "sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==", + "dev": true, + "requires": { + "agentkeepalive": "^4.2.1", + "cacache": "^16.1.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^2.0.3", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^9.0.0" + } + }, + "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" + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "dev": true + }, + "mem": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/mem/-/mem-5.1.1.tgz", + "integrity": "sha512-qvwipnozMohxLXG1pOqoLiZKNkC4r4qqRucSoDwXowsNGDSULiqFTRUF05vcZWnwJSG22qTsynQhxbaMtnX9gw==", + "dev": true, + "requires": { + "map-age-cleaner": "^0.1.3", + "mimic-fn": "^2.1.0", + "p-is-promise": "^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==", + "dev": true + } + } + }, + "mem-fs": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/mem-fs/-/mem-fs-2.2.1.tgz", + "integrity": "sha512-yiAivd4xFOH/WXlUi6v/nKopBh1QLzwjFi36NK88cGt/PRXI8WeBASqY+YSjIVWvQTx3hR8zHKDBMV6hWmglNA==", + "dev": true, + "requires": { + "@types/node": "^15.6.1", + "@types/vinyl": "^2.0.4", + "vinyl": "^2.0.1", + "vinyl-file": "^3.0.0" + }, + "dependencies": { + "@types/node": { + "version": "15.14.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-15.14.9.tgz", + "integrity": "sha512-qjd88DrCxupx/kJD5yQgZdcYKZKSIGBVDIBE1/LTGcNm3d2Np/jxojkdePDdfnBHJc5W7vSMpbJ1aB7p/Py69A==", + "dev": true + } + } + }, + "mem-fs-editor": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/mem-fs-editor/-/mem-fs-editor-9.5.0.tgz", + "integrity": "sha512-7p+bBDqsSisO20YIZf2ntYvST27fFJINn7CKE21XdPUQDcLV62b/yB5sTOooQeEoiZ3rldZQ+4RfONgL/gbRoA==", + "dev": true, + "requires": { + "binaryextensions": "^4.16.0", + "commondir": "^1.0.1", + "deep-extend": "^0.6.0", + "ejs": "^3.1.8", + "globby": "^11.1.0", + "isbinaryfile": "^4.0.8", + "minimatch": "^3.1.2", + "multimatch": "^5.0.0", + "normalize-path": "^3.0.0", + "textextensions": "^5.13.0" + }, + "dependencies": { + "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" + } + }, + "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" + } + } + } + }, + "memfs": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.7.tgz", + "integrity": "sha512-ygaiUSNalBX85388uskeCyhSAoOSgzBbtVCr9jA2RROssFL9Q19/ZXFqS+2Th2sr1ewNIWgFdLzLC3Yl1Zv+lw==", + "dev": true, + "requires": { + "fs-monkey": "^1.0.3" + } + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==", + "dev": true + }, + "merge-jsons-webpack-plugin": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/merge-jsons-webpack-plugin/-/merge-jsons-webpack-plugin-2.0.1.tgz", + "integrity": "sha512-8GP8rpOX3HSFsm7Gx+b3OAQR7yhgeAQvMqcZOJ+/cQIrqdak1c42a2T2vyeee8pzGPBf7pMLumthPh4CHgv2BA==", + "dev": true, + "requires": { + "glob": "7.1.1" + }, + "dependencies": { + "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" + } + }, + "glob": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.1.tgz", + "integrity": "sha512-mRyN/EsN2SyNhKWykF3eEGhDpeNplMWaW18Bmh76tnOqk5TbELAVwFAYOCmKVssOYFrYvvLMguiA+NXO3ZTuVA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.2", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "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" + } + } + } + }, + "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": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "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==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "mime-db": "1.52.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==", + "dev": true + }, + "mini-css-extract-plugin": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.6.1.tgz", + "integrity": "sha512-wd+SD57/K6DiV7jIR34P+s3uckTRuQvx0tKPcvjFlrEylk6P4mQ2KSWk1hblj1Kxaqok7LogKOieygXqBczNlg==", + "dev": true, + "requires": { + "schema-utils": "^4.0.0" + }, + "dependencies": { + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } + } + } + }, + "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": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true + }, + "minipass": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.4.tgz", + "integrity": "sha512-I9WPbWHCGu8W+6k1ZiGpPu0GkoKBeorkfKNuAFBNS1HNFJvke82sxvI5bzcCNpWPorkOO5QQ+zomzzwRxejXiw==", + "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-fetch": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-2.1.2.tgz", + "integrity": "sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==", + "dev": true, + "requires": { + "encoding": "^0.1.13", + "minipass": "^3.1.6", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + } + }, + "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-json-stream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minipass-json-stream/-/minipass-json-stream-1.0.1.tgz", + "integrity": "sha512-ODqY18UZt/I8k+b7rl2AENgbWE8IDYam+undIJONvigAz8KR5GWblsFTEfQs0WODsjbSXWlm+JHEv8Gr6Tfdbg==", + "dev": true, + "requires": { + "jsonparse": "^1.3.1", + "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" + } + }, + "minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "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" + } + }, + "mitt": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-1.2.0.tgz", + "integrity": "sha512-r6lj77KlwqLhIUku9UWYes7KJtsczvolZkzp8hbaDPPaE24OmWl5s539Mytlj22siEQKosZ26qCBgda2PKwoJw==", + "dev": true + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true + }, + "mkdirp-infer-owner": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mkdirp-infer-owner/-/mkdirp-infer-owner-2.0.0.tgz", + "integrity": "sha512-sdqtiFt3lkOaYvTXSRIUjkIdPTcxgv5+fgqYE/5qgwdw12cOrAuzzgzvVExIkH/ul1oeHN3bCLOWSG3XOqbKKw==", + "dev": true, + "requires": { + "chownr": "^2.0.0", + "infer-owner": "^1.0.4", + "mkdirp": "^1.0.3" + } + }, + "mrmime": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz", + "integrity": "sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==", + "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==" + }, + "multicast-dns": { + "version": "7.2.5", + "resolved": "https://registry.npmjs.org/multicast-dns/-/multicast-dns-7.2.5.tgz", + "integrity": "sha512-2eznPJP8z2BFLX50tf0LuODrpINqP1RVIm/CObbTcBRITQgmC/TjcREF1NeTBzIcR5XO/ukWo+YHOjBbFwIupg==", + "dev": true, + "requires": { + "dns-packet": "^5.2.2", + "thunky": "^1.0.2" + } + }, + "multimatch": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/multimatch/-/multimatch-5.0.0.tgz", + "integrity": "sha512-ypMKuglUrZUD99Tk2bUQ+xNQj43lPEfAeX2o9cTteAmShXy2VHDJpuwu1o0xqoKCt9jLVAvwyFKdLTPXKAfJyA==", + "dev": true, + "requires": { + "@types/minimatch": "^3.0.3", + "array-differ": "^3.0.0", + "array-union": "^2.1.0", + "arrify": "^2.0.1", + "minimatch": "^3.0.4" + }, + "dependencies": { + "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" + } + }, + "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" + } + } + } + }, + "mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "nanoid": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz", + "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==", + "dev": true + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true + }, + "needle": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/needle/-/needle-3.1.0.tgz", + "integrity": "sha512-gCE9weDhjVGCRqS8dwDR/D3GTAeyXLXuqp7I8EzH6DllZGXSUyxuqqLh+YX9rMAWaaTFyVAg6rHGL25dqvczKw==", + "dev": true, + "optional": true, + "requires": { + "debug": "^3.2.6", + "iconv-lite": "^0.6.3", + "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" + } + }, + "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, + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true, + "optional": true + } + } + }, + "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 + }, + "ng2-charts": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/ng2-charts/-/ng2-charts-4.0.0.tgz", + "integrity": "sha512-1COLMs1UH8XIurk9C3pBQW3zH4RM3ggPtaC5vGjEmRGZ2cK/j8DqpzN4xMqyk0KB4D2vw/ZejgXmxxZ4Ie58Rw==", + "requires": { + "lodash-es": "^4.17.15", + "tslib": "^2.3.0" + } + }, + "ngx-infinite-scroll": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/ngx-infinite-scroll/-/ngx-infinite-scroll-14.0.0.tgz", + "integrity": "sha512-YZB5PBPXSERNtCGQRZTVflbgkh5asp01NPfC8KPItemmQik1Ip8ZCCbcyHA77TDTdilmaiu8TbguA3geg/LMWw==", + "requires": { + "tslib": "^2.3.0" + } + }, + "ngx-webstorage": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/ngx-webstorage/-/ngx-webstorage-10.0.1.tgz", + "integrity": "sha512-OWmzAzby+/UrbRY/5d229Y4NzFn1a/u2WSEeZqzY5lwB/3d8ODZ6mlW/BZGIuuZ48Hp8tXMM3pFCz9+pEyzvDA==", + "requires": { + "tslib": "^2.0.0" + } + }, + "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" + } + }, + "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==", + "dev": true, + "requires": { + "whatwg-url": "^5.0.0" + }, + "dependencies": { + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "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==", + "dev": true + }, + "node-gyp": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-9.1.0.tgz", + "integrity": "sha512-HkmN0ZpQJU7FLbJauJTHkHlSVAXlNGDAzH/VYFZGDOnFyn/Na3GlNJfkudmufOdS6/jNFhy88ObzL7ERz9es1g==", + "dev": true, + "requires": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^10.0.3", + "nopt": "^5.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "dependencies": { + "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" + } + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "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" + } + } + } + }, + "node-gyp-build": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.5.0.tgz", + "integrity": "sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg==", + "dev": true, + "optional": true + }, + "node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true + }, + "node-notifier": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-9.0.1.tgz", + "integrity": "sha512-fPNFIp2hF/Dq7qLDzSg4vZ0J4e9v60gJR+Qx7RbjbWqzPDdEqeVpEx5CFeDAELIl+A/woaaNn1fQ5nEVerMxJg==", + "dev": true, + "requires": { + "growly": "^1.3.0", + "is-wsl": "^2.2.0", + "semver": "^7.3.2", + "shellwords": "^0.1.1", + "uuid": "^8.3.0", + "which": "^2.0.2" + } + }, + "node-releases": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.6.tgz", + "integrity": "sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg==" + }, + "nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dev": true, + "requires": { + "abbrev": "1" + } + }, + "normalize-package-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-4.0.1.tgz", + "integrity": "sha512-EBk5QKKuocMJhB3BILuKhmaPjI8vNRSpIfO9woLC6NyHVkKKdVEdAO1mrT0ZfxNR1lKwCcTkuZfmGIFdizZ8Pg==", + "dev": true, + "requires": { + "hosted-git-info": "^5.0.0", + "is-core-module": "^2.8.1", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + }, + "normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "dev": true + }, + "npm-bundled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.2.tgz", + "integrity": "sha512-x5DHup0SuyQcmL3s7Rx/YQ8sbw/Hzg0rj48eN0dV7hf5cmQq5PXIeioroH3raV1QC1yh3uTYuMThvEQF3iKgGQ==", + "dev": true, + "requires": { + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-install-checks": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-5.0.0.tgz", + "integrity": "sha512-65lUsMI8ztHCxFz5ckCEC44DRvEGdZX5usQFriauxHEwt7upv1FKaQEmAtU0YnOAdwuNWCmk64xYiQABNrEyLA==", + "dev": true, + "requires": { + "semver": "^7.1.1" + } + }, + "npm-normalize-package-bin": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", + "integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", + "dev": true + }, + "npm-package-arg": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-9.1.0.tgz", + "integrity": "sha512-4J0GL+u2Nh6OnhvUKXRr2ZMG4lR8qtLp+kv7UiV00Y+nGiSxtttCyIRHCt5L5BNkXQld/RceYItau3MDOoGiBw==", + "dev": true, + "requires": { + "hosted-git-info": "^5.0.0", + "proc-log": "^2.0.1", + "semver": "^7.3.5", + "validate-npm-package-name": "^4.0.0" + } + }, + "npm-packlist": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-5.1.3.tgz", + "integrity": "sha512-263/0NGrn32YFYi4J533qzrQ/krmmrWwhKkzwTuM4f/07ug51odoaNjUexxO4vxlzURHcmYMH1QjvHjsNDKLVg==", + "dev": true, + "requires": { + "glob": "^8.0.1", + "ignore-walk": "^5.0.1", + "npm-bundled": "^2.0.0", + "npm-normalize-package-bin": "^2.0.0" + }, + "dependencies": { + "npm-bundled": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-2.0.1.tgz", + "integrity": "sha512-gZLxXdjEzE/+mOstGDqR6b0EkhJ+kM6fxM6vUuckuctuVPh80Q6pw/rSZj9s4Gex9GxWtIicO1pc8DB9KZWudw==", + "dev": true, + "requires": { + "npm-normalize-package-bin": "^2.0.0" + } + }, + "npm-normalize-package-bin": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-2.0.0.tgz", + "integrity": "sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ==", + "dev": true + } + } + }, + "npm-pick-manifest": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-7.0.1.tgz", + "integrity": "sha512-IA8+tuv8KujbsbLQvselW2XQgmXWS47t3CB0ZrzsRZ82DbDfkcFunOaPm4X7qNuhMfq+FmV7hQT4iFVpHqV7mg==", + "dev": true, + "requires": { + "npm-install-checks": "^5.0.0", + "npm-normalize-package-bin": "^1.0.1", + "npm-package-arg": "^9.0.0", + "semver": "^7.3.5" + } + }, + "npm-registry-fetch": { + "version": "13.3.1", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-13.3.1.tgz", + "integrity": "sha512-eukJPi++DKRTjSBRcDZSDDsGqRK3ehbxfFUcgaRd0Yp6kRwOwh2WVn0r+8rMB4nnuzvAk6rQVzl6K5CkYOmnvw==", + "dev": true, + "requires": { + "make-fetch-happen": "^10.0.6", + "minipass": "^3.1.6", + "minipass-fetch": "^2.0.3", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.1.2", + "npm-package-arg": "^9.0.1", + "proc-log": "^2.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" + } + }, + "npmlog": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", + "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", + "dev": true, + "requires": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + } + }, + "nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "requires": { + "boolbase": "^1.0.0" + } + }, + "nwsapi": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.2.tgz", + "integrity": "sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw==", + "dev": true + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true + }, + "object-inspect": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "dev": true + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true + }, + "object.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.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": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "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": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "requires": { + "wrappy": "1" + } + }, + "one-time": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", + "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", + "dev": true, + "requires": { + "fn.name": "1.x.x" + } + }, + "onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "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==", + "dev": true + } + } + }, + "open": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", + "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", + "dev": true, + "requires": { + "define-lazy-prop": "^2.0.0", + "is-docker": "^2.1.1", + "is-wsl": "^2.2.0" + } + }, + "opener": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/opener/-/opener-1.5.2.tgz", + "integrity": "sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==", + "dev": true + }, + "openurl": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/openurl/-/openurl-1.1.1.tgz", + "integrity": "sha512-d/gTkTb1i1GKz5k3XE3XFV/PxQ1k45zDqGP2OA7YhgsaLoqm6qRvARAZOFer1fcXritWlGBRCu/UgeS4HAnXAA==", + "dev": true + }, + "opn": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/opn/-/opn-5.3.0.tgz", + "integrity": "sha512-bYJHo/LOmoTd+pfiYhfZDnf9zekVJrY+cnS2a5F2x+w5ppvTqObojTP7WiFG+kVZs9Inw+qQ/lw7TroWwhdd2g==", + "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": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==", + "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" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.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==", + "dev": true, + "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==", + "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==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "os-locale": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-5.0.0.tgz", + "integrity": "sha512-tqZcNEDAIZKBEPnHPlVDvKrp7NzgLi7jRmhKiUoa2NUmhl13FtkAGLUVR+ZsYvApBQdBfYm43A4tXXQ4IrYLBA==", + "dev": true, + "requires": { + "execa": "^4.0.0", + "lcid": "^3.0.0", + "mem": "^5.0.0" + } + }, + "os-name": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/os-name/-/os-name-4.0.1.tgz", + "integrity": "sha512-xl9MAoU97MH1Xt5K9ERft2YfCAoaO6msy1OBA0ozxEC0x0TmIoE6K3QvgJMMZA9yKGLmHXNY/YZoDbiGDj4zYw==", + "dev": true, + "requires": { + "macos-release": "^2.5.0", + "windows-release": "^4.0.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "dev": true + }, + "ospath": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/ospath/-/ospath-1.2.2.tgz", + "integrity": "sha512-o6E5qJV5zkAbIDNhGSIlyOhScKXgQrSRMilfph0clDfM0nEnBOlKlH4sWDmG95BW/CvwNz0vmm7dJVtU2KlMiA==", + "dev": true + }, + "p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha512-wB3wfAxZpk2AzOfUMJNL+d36xothRSyj8EXOa4f6GMqYDN9BJaaSISbsk+wS9abmnebVw95C2Kb5t85UmpCxuw==", + "dev": true + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", + "dev": true + }, + "p-is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.1.0.tgz", + "integrity": "sha512-Y3W0wlRPK8ZMRbNq97l4M5otioeA5lm1z7bkNkxCka8HSPjR0xRWmpCmc9utiaLP9Jb1eD8BgeIxTW4AIF45Pg==", + "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": "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" + }, + "dependencies": { + "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-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-queue": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz", + "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==", + "dev": true, + "requires": { + "eventemitter3": "^4.0.4", + "p-timeout": "^3.2.0" + } + }, + "p-retry": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", + "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", + "dev": true, + "requires": { + "@types/retry": "0.12.0", + "retry": "^0.13.1" + }, + "dependencies": { + "retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "dev": true + } + } + }, + "p-timeout": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", + "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", + "dev": true, + "requires": { + "p-finally": "^1.0.0" + } + }, + "p-transform": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-transform/-/p-transform-1.3.0.tgz", + "integrity": "sha512-UJKdSzgd3KOnXXAtqN5+/eeHcvTn1hBkesEmElVgvO/NAYcxAvmjzIGmnNd3Tb/gRAvMBdNRFD4qAWdHxY6QXg==", + "dev": true, + "requires": { + "debug": "^4.3.2", + "p-queue": "^6.6.2" + } + }, + "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==", + "dev": true + }, + "pacote": { + "version": "13.6.2", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-13.6.2.tgz", + "integrity": "sha512-Gu8fU3GsvOPkak2CkbojR7vjs3k3P9cA6uazKTHdsdV0gpCEQq2opelnEv30KRQWgVzP5Vd/5umjcedma3MKtg==", + "dev": true, + "requires": { + "@npmcli/git": "^3.0.0", + "@npmcli/installed-package-contents": "^1.0.7", + "@npmcli/promise-spawn": "^3.0.0", + "@npmcli/run-script": "^4.1.0", + "cacache": "^16.0.0", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "infer-owner": "^1.0.4", + "minipass": "^3.1.6", + "mkdirp": "^1.0.4", + "npm-package-arg": "^9.0.0", + "npm-packlist": "^5.1.0", + "npm-pick-manifest": "^7.0.0", + "npm-registry-fetch": "^13.0.1", + "proc-log": "^2.0.0", + "promise-retry": "^2.0.1", + "read-package-json": "^5.0.0", + "read-package-json-fast": "^2.0.3", + "rimraf": "^3.0.2", + "ssri": "^9.0.0", + "tar": "^6.1.11" + } + }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true + }, + "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-cache-control": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-cache-control/-/parse-cache-control-1.0.1.tgz", + "integrity": "sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg==", + "dev": true + }, + "parse-conflict-json": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/parse-conflict-json/-/parse-conflict-json-2.0.2.tgz", + "integrity": "sha512-jDbRGb00TAPFsKWCpZZOT93SxVP9nONOSgES3AevqRq/CHvavEBvKAjxX9p5Y5F0RZLxH9Ufd9+RwtCsa+lFDA==", + "dev": true, + "requires": { + "json-parse-even-better-errors": "^2.3.1", + "just-diff": "^5.0.1", + "just-diff-apply": "^5.2.0" + } + }, + "parse-gitignore": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parse-gitignore/-/parse-gitignore-2.0.0.tgz", + "integrity": "sha512-RmVuCHWsfu0QPNW+mraxh/xjQVw/lhUCUru8Zni3Ctq3AoMhpDTq0OVdKS6iesd6Kqb7viCV3isAL43dciOSog==", + "dev": true + }, + "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 + }, + "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 + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "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": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==", + "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": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", + "dev": true + }, + "picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" + }, + "pidtree": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz", + "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==", + "dev": true + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true + }, + "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.2.0", + "resolved": "https://registry.npmjs.org/piscina/-/piscina-3.2.0.tgz", + "integrity": "sha512-yn/jMdHRw+q2ZJhFhyqsmANcbF6V2QwmD84c6xRau+QpQOmtrBCoRGdvTfeuFDYXB5W2m6MfLkjkvQa9lUSmIA==", + "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" + } + }, + "pkg-up": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz", + "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "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==", + "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" + } + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "dev": true + } + } + }, + "pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "dev": true + }, + "portscanner": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/portscanner/-/portscanner-2.2.0.tgz", + "integrity": "sha512-IFroCz/59Lqa2uBvzK3bKDbDDIEaAY8XJ1jFxcLWTqosrsc32//P4VuSB2vZXoHiHqOmx8B5L5hnKOxL/7FlPw==", + "dev": true, + "requires": { + "async": "^2.6.0", + "is-number-like": "^1.0.3" + }, + "dependencies": { + "async": { + "version": "2.6.4", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.4.tgz", + "integrity": "sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==", + "dev": true, + "requires": { + "lodash": "^4.17.14" + } + } + } + }, + "positioning": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/positioning/-/positioning-2.0.1.tgz", + "integrity": "sha512-DsAgM42kV/ObuwlRpAzDTjH9E8fGKkMDJHWFX+kfNXSxh7UCCQxEmdjv/Ws5Ft1XDnt3JT8fIDYeKNSE2TbttA==" + }, + "postcss": { + "version": "8.4.16", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.16.tgz", + "integrity": "sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==", + "dev": true, + "requires": { + "nanoid": "^3.3.4", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + } + }, + "postcss-attribute-case-insensitive": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.2.tgz", + "integrity": "sha512-XIidXV8fDr0kKt28vqki84fRK8VW8eTuIa4PChv2MqKuT6C9UjmSKzen6KaWhWEoYvwxFCa7n/tC1SZ3tyq4SQ==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.10" + } + }, + "postcss-clamp": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-clamp/-/postcss-clamp-4.1.0.tgz", + "integrity": "sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-color-functional-notation": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/postcss-color-functional-notation/-/postcss-color-functional-notation-4.2.4.tgz", + "integrity": "sha512-2yrTAUZUab9s6CpxkxC4rVgFEVaR6/2Pipvi6qcgvnYiVqZcbDHEoBDhrXzyb7Efh2CCfHQNtcqWcIruDTIUeg==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-color-hex-alpha": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/postcss-color-hex-alpha/-/postcss-color-hex-alpha-8.0.4.tgz", + "integrity": "sha512-nLo2DCRC9eE4w2JmuKgVA3fGL3d01kGq752pVALF68qpGLmx2Qrk91QTKkdUqqp45T1K1XV8IhQpcu1hoAQflQ==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-color-rebeccapurple": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-color-rebeccapurple/-/postcss-color-rebeccapurple-7.1.1.tgz", + "integrity": "sha512-pGxkuVEInwLHgkNxUc4sdg4g3py7zUeCQ9sMfwyHAT+Ezk8a4OaaVZ8lIY5+oNqA/BXXgLyXv0+5wHP68R79hg==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-custom-media": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/postcss-custom-media/-/postcss-custom-media-8.0.2.tgz", + "integrity": "sha512-7yi25vDAoHAkbhAzX9dHx2yc6ntS4jQvejrNcC+csQJAXjj15e7VcWfMgLqBNAbOvqi5uIa9huOVwdHbf+sKqg==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-custom-properties": { + "version": "12.1.8", + "resolved": "https://registry.npmjs.org/postcss-custom-properties/-/postcss-custom-properties-12.1.8.tgz", + "integrity": "sha512-8rbj8kVu00RQh2fQF81oBqtduiANu4MIxhyf0HbbStgPtnFlWn0yiaYTpLHrPnJbffVY1s9apWsIoVZcc68FxA==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-custom-selectors": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/postcss-custom-selectors/-/postcss-custom-selectors-6.0.3.tgz", + "integrity": "sha512-fgVkmyiWDwmD3JbpCmB45SvvlCD6z9CG6Ie6Iere22W5aHea6oWa7EM2bpnv2Fj3I94L3VbtvX9KqwSi5aFzSg==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.4" + } + }, + "postcss-dir-pseudo-class": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/postcss-dir-pseudo-class/-/postcss-dir-pseudo-class-6.0.5.tgz", + "integrity": "sha512-eqn4m70P031PF7ZQIvSgy9RSJ5uI2171O/OO/zcRNYpJbvaeKFUlar1aJ7rmgiQtbm0FSPsRewjpdS0Oew7MPA==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.10" + } + }, + "postcss-double-position-gradients": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/postcss-double-position-gradients/-/postcss-double-position-gradients-3.1.2.tgz", + "integrity": "sha512-GX+FuE/uBR6eskOK+4vkXgT6pDkexLokPaz/AbJna9s5Kzp/yl488pKPjhy0obB475ovfT1Wv8ho7U/cHNaRgQ==", + "dev": true, + "requires": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-env-function": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/postcss-env-function/-/postcss-env-function-4.0.6.tgz", + "integrity": "sha512-kpA6FsLra+NqcFnL81TnsU+Z7orGtDTxcOhl6pwXeEq1yFPpRMkCDpHhrz8CFQDr/Wfm0jLiNQ1OsGGPjlqPwA==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-focus-visible": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/postcss-focus-visible/-/postcss-focus-visible-6.0.4.tgz", + "integrity": "sha512-QcKuUU/dgNsstIK6HELFRT5Y3lbrMLEOwG+A4s5cA+fx3A3y/JTq3X9LaOj3OC3ALH0XqyrgQIgey/MIZ8Wczw==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.9" + } + }, + "postcss-focus-within": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-focus-within/-/postcss-focus-within-5.0.4.tgz", + "integrity": "sha512-vvjDN++C0mu8jz4af5d52CB184ogg/sSxAFS+oUJQq2SuCe7T5U2iIsVJtsCp2d6R4j0jr5+q3rPkBVZkXD9fQ==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.9" + } + }, + "postcss-font-variant": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", + "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", + "dev": true, + "requires": {} + }, + "postcss-gap-properties": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.5.tgz", + "integrity": "sha512-IuE6gKSdoUNcvkGIqdtjtcMtZIFyXZhmFd5RUlg97iVEvp1BZKV5ngsAjCjrVy+14uhGBQl9tzmi1Qwq4kqVOg==", + "dev": true, + "requires": {} + }, + "postcss-image-set-function": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/postcss-image-set-function/-/postcss-image-set-function-4.0.7.tgz", + "integrity": "sha512-9T2r9rsvYzm5ndsBE8WgtrMlIT7VbtTfE7b3BQnudUqnBcBo7L758oc+o+pdj/dUV0l5wjwSdjeOH2DZtfv8qw==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-import": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.1.0.tgz", + "integrity": "sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + } + }, + "postcss-initial": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz", + "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==", + "dev": true, + "requires": {} + }, + "postcss-lab-function": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/postcss-lab-function/-/postcss-lab-function-4.2.1.tgz", + "integrity": "sha512-xuXll4isR03CrQsmxyz92LJB2xX9n+pZJ5jE9JgcnmsCammLyKdlzrBin+25dy6wIjfhJpKBAN80gsTlCgRk2w==", + "dev": true, + "requires": { + "@csstools/postcss-progressive-custom-properties": "^1.1.0", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-loader": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-loader/-/postcss-loader-7.0.1.tgz", + "integrity": "sha512-VRviFEyYlLjctSM93gAZtcJJ/iSkPZ79zWbN/1fSH+NisBByEiVLqpdVDrPLVSi8DX0oJo12kL/GppTBdKVXiQ==", + "dev": true, + "requires": { + "cosmiconfig": "^7.0.0", + "klona": "^2.0.5", + "semver": "^7.3.7" + } + }, + "postcss-logical": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.4.tgz", + "integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==", + "dev": true, + "requires": {} + }, + "postcss-media-minmax": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz", + "integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==", + "dev": true, + "requires": {} + }, + "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": "10.1.10", + "resolved": "https://registry.npmjs.org/postcss-nesting/-/postcss-nesting-10.1.10.tgz", + "integrity": "sha512-lqd7LXCq0gWc0wKXtoKDru5wEUNjm3OryLVNRZ8OnW8km6fSNUuFrjEhU3nklxXE2jvd4qrox566acgh+xQt8w==", + "dev": true, + "requires": { + "@csstools/selector-specificity": "^2.0.0", + "postcss-selector-parser": "^6.0.10" + } + }, + "postcss-opacity-percentage": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/postcss-opacity-percentage/-/postcss-opacity-percentage-1.1.2.tgz", + "integrity": "sha512-lyUfF7miG+yewZ8EAk9XUBIlrHyUE6fijnesuz+Mj5zrIHIEw6KcIZSOk/elVMqzLvREmXB83Zi/5QpNRYd47w==", + "dev": true + }, + "postcss-overflow-shorthand": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-overflow-shorthand/-/postcss-overflow-shorthand-3.0.4.tgz", + "integrity": "sha512-otYl/ylHK8Y9bcBnPLo3foYFLL6a6Ak+3EQBPOTR7luMYCOsiVTUk1iLvNf6tVPNGXcoL9Hoz37kpfriRIFb4A==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-page-break": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", + "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", + "dev": true, + "requires": {} + }, + "postcss-place": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/postcss-place/-/postcss-place-7.0.5.tgz", + "integrity": "sha512-wR8igaZROA6Z4pv0d+bvVrvGY4GVHihBCBQieXFY3kuSuMyOmEnnfFzHl/tQuqHZkfkIVBEbDvYcFfHmpSet9g==", + "dev": true, + "requires": { + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-preset-env": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/postcss-preset-env/-/postcss-preset-env-7.8.0.tgz", + "integrity": "sha512-leqiqLOellpLKfbHkD06E04P6d9ZQ24mat6hu4NSqun7WG0UhspHR5Myiv/510qouCjoo4+YJtNOqg5xHaFnCA==", + "dev": true, + "requires": { + "@csstools/postcss-cascade-layers": "^1.0.5", + "@csstools/postcss-color-function": "^1.1.1", + "@csstools/postcss-font-format-keywords": "^1.0.1", + "@csstools/postcss-hwb-function": "^1.0.2", + "@csstools/postcss-ic-unit": "^1.0.1", + "@csstools/postcss-is-pseudo-class": "^2.0.7", + "@csstools/postcss-nested-calc": "^1.0.0", + "@csstools/postcss-normalize-display-values": "^1.0.1", + "@csstools/postcss-oklab-function": "^1.1.1", + "@csstools/postcss-progressive-custom-properties": "^1.3.0", + "@csstools/postcss-stepped-value-functions": "^1.0.1", + "@csstools/postcss-text-decoration-shorthand": "^1.0.0", + "@csstools/postcss-trigonometric-functions": "^1.0.2", + "@csstools/postcss-unset-value": "^1.0.2", + "autoprefixer": "^10.4.8", + "browserslist": "^4.21.3", + "css-blank-pseudo": "^3.0.3", + "css-has-pseudo": "^3.0.4", + "css-prefers-color-scheme": "^6.0.3", + "cssdb": "^7.0.0", + "postcss-attribute-case-insensitive": "^5.0.2", + "postcss-clamp": "^4.1.0", + "postcss-color-functional-notation": "^4.2.4", + "postcss-color-hex-alpha": "^8.0.4", + "postcss-color-rebeccapurple": "^7.1.1", + "postcss-custom-media": "^8.0.2", + "postcss-custom-properties": "^12.1.8", + "postcss-custom-selectors": "^6.0.3", + "postcss-dir-pseudo-class": "^6.0.5", + "postcss-double-position-gradients": "^3.1.2", + "postcss-env-function": "^4.0.6", + "postcss-focus-visible": "^6.0.4", + "postcss-focus-within": "^5.0.4", + "postcss-font-variant": "^5.0.0", + "postcss-gap-properties": "^3.0.5", + "postcss-image-set-function": "^4.0.7", + "postcss-initial": "^4.0.1", + "postcss-lab-function": "^4.2.1", + "postcss-logical": "^5.0.4", + "postcss-media-minmax": "^5.0.0", + "postcss-nesting": "^10.1.10", + "postcss-opacity-percentage": "^1.1.2", + "postcss-overflow-shorthand": "^3.0.4", + "postcss-page-break": "^3.0.4", + "postcss-place": "^7.0.5", + "postcss-pseudo-class-any-link": "^7.1.6", + "postcss-replace-overflow-wrap": "^4.0.0", + "postcss-selector-not": "^6.0.1", + "postcss-value-parser": "^4.2.0" + } + }, + "postcss-pseudo-class-any-link": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/postcss-pseudo-class-any-link/-/postcss-pseudo-class-any-link-7.1.6.tgz", + "integrity": "sha512-9sCtZkO6f/5ML9WcTLcIyV1yz9D1rf0tWc+ulKcvV30s0iZKS/ONyETvoWsr6vnrmW+X+KmuK3gV/w5EWnT37w==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.10" + } + }, + "postcss-replace-overflow-wrap": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", + "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", + "dev": true, + "requires": {} + }, + "postcss-selector-not": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-selector-not/-/postcss-selector-not-6.0.1.tgz", + "integrity": "sha512-1i9affjAe9xu/y9uqWH+tD4r6/hDaXJruk8xn2x1vzxC2U3J3LKO3zJW4CyxlNhA56pADJ/djpEwpH1RClI2rQ==", + "dev": true, + "requires": { + "postcss-selector-parser": "^6.0.10" + } + }, + "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-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 + }, + "preferred-pm": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/preferred-pm/-/preferred-pm-3.0.3.tgz", + "integrity": "sha512-+wZgbxNES/KlJs9q40F/1sfOd/j7f1O9JaHcW5Dsn3aUUOZg3L2bjpVUcKV2jvtElYfoTuQiNeMfQJ4kwUAhCQ==", + "dev": true, + "requires": { + "find-up": "^5.0.0", + "find-yarn-workspace-root2": "1.2.16", + "path-exists": "^4.0.0", + "which-pm": "2.0.0" + }, + "dependencies": { + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + } + } + }, + "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 + }, + "prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true + }, + "prettier-plugin-java": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/prettier-plugin-java/-/prettier-plugin-java-2.1.0.tgz", + "integrity": "sha512-fALvx3PoabX6gar5Ock/reSEJVTmmJXowkvEXWhn6AxXnLfXoZxKqPNsUdQ+jcVaXO7jnQTW39hVGBnbfgFj+A==", + "dev": true, + "requires": { + "java-parser": "2.0.4", + "lodash": "4.17.21", + "prettier": "2.8.4" + }, + "dependencies": { + "prettier": { + "version": "2.8.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.4.tgz", + "integrity": "sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw==", + "dev": true + } + } + }, + "prettier-plugin-packagejson": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/prettier-plugin-packagejson/-/prettier-plugin-packagejson-2.4.3.tgz", + "integrity": "sha512-kPeeviJiwy0BgOSk7No8NmzzXfW4R9FYWni6ziA5zc1kGVVrKnBzMZdu2TUhI+I7h8/5Htt3vARYOk7KKJTTNQ==", + "dev": true, + "requires": { + "sort-package-json": "2.4.1", + "synckit": "0.8.5" + } + }, + "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": "28.1.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", + "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", + "dev": true, + "requires": { + "@jest/schemas": "^28.1.3", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "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 + } + } + }, + "proc-log": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-2.0.1.tgz", + "integrity": "sha512-Kcmo2FhfDTXdcbfDH76N7uBYHINxc/8GW7UAVuVP9I+Va3uHSerrnKV6dLooga/gh7GlgzuCCr/eoldnL1muGw==", + "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==", + "dev": true + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true + }, + "promise": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-8.2.0.tgz", + "integrity": "sha512-+CMAlLHqwRYwBMXKCP+o8ns7DN+xHDUiI+0nArsiJ9y+kJVPLFxEaSw6Ha9s9H0tftxg2Yzl25wqj9G7m5wLZg==", + "dev": true, + "requires": { + "asap": "~2.0.6" + } + }, + "promise-all-reject-late": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-all-reject-late/-/promise-all-reject-late-1.0.1.tgz", + "integrity": "sha512-vuf0Lf0lOxyQREH7GDIOUMLS7kz+gs8i6B+Yi8dC68a2sychGrHTJYghMBD6k7eUcH0H5P73EckCA48xijWqXw==", + "dev": true + }, + "promise-call-limit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-call-limit/-/promise-call-limit-1.0.1.tgz", + "integrity": "sha512-3+hgaa19jzCGLuSCbieeRsu5C2joKfYn8pY6JAuXFRVfF4IO+L7UPpFWNTeWT9pM7uhskvbPPd/oEOktCn317Q==", + "dev": true + }, + "promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "dev": true + }, + "promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "dev": true, + "requires": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + } + }, + "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" + } + }, + "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" + }, + "dependencies": { + "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 + } + } + }, + "proxy-from-env": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.0.0.tgz", + "integrity": "sha512-F2JHgJQ1iqwnHDcQjVBsq3n/uoaFL+iPW/eAeL7kVxy/2RrWaN4WroKjjvbsoRtv0ftelNyC01bjRhn/bhcf4A==", + "dev": true + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==", + "dev": true, + "optional": true + }, + "psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", + "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==", + "dev": true, + "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==", + "dev": true + }, + "qs": { + "version": "6.11.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.1.tgz", + "integrity": "sha512-0wsrzgTz/kAVIeuxSjnpGC56rzYtr6JT/2BwEvMaPhFIoYa1aGO8LbzuU1R0uUYQkLpWBTOj0l/CLAJB64J6nQ==", + "dev": true, + "requires": { + "side-channel": "^1.0.4" + } + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", + "dev": true + }, + "querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true + }, + "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 + }, + "randexp": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.5.3.tgz", + "integrity": "sha512-U+5l2KrcMNOUPYvazA3h5ekF80FHTUG+87SEAmHZmolh1M+i/WyTCxVzmi+tidIa1tM4BSe8g2Y/D3loWDjj+w==", + "dev": true, + "requires": { + "drange": "^1.0.2", + "ret": "^0.2.0" + } + }, + "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.5.1", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", + "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", + "dev": true, + "requires": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", + "dev": true + }, + "read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "requires": { + "pify": "^2.3.0" + } + }, + "read-cmd-shim": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/read-cmd-shim/-/read-cmd-shim-3.0.1.tgz", + "integrity": "sha512-kEmDUoYf/CDy8yZbLTmhB1X9kkjf9Q80PCNsDMb7ufrGd6zZSQA1+UyjrO+pZm5K/S4OXCWJeiIt1JA8kAsa6g==", + "dev": true + }, + "read-package-json": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/read-package-json/-/read-package-json-5.0.2.tgz", + "integrity": "sha512-BSzugrt4kQ/Z0krro8zhTwV1Kd79ue25IhNN/VtHFy1mG/6Tluyi+msc0UpwaoQzxSHa28mntAjIZY6kEgfR9Q==", + "dev": true, + "requires": { + "glob": "^8.0.1", + "json-parse-even-better-errors": "^2.3.1", + "normalize-package-data": "^4.0.0", + "npm-normalize-package-bin": "^2.0.0" + }, + "dependencies": { + "npm-normalize-package-bin": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-2.0.0.tgz", + "integrity": "sha512-awzfKUO7v0FscrSpRoogyNm0sajikhBWpU0QMrW09AMi9n1PoKU6WaIqUzuJSQnpciZZmJ/jMZ2Egfmb/9LiWQ==", + "dev": true + } + } + }, + "read-package-json-fast": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/read-package-json-fast/-/read-package-json-fast-2.0.3.tgz", + "integrity": "sha512-W/BKtbL+dUjTuRL2vziuYhp76s5HZ9qQhd/dKfWIZveD0O40453QNyZhC0e63lqZrAQ4jiOapVoeJ7JrszenQQ==", + "dev": true, + "requires": { + "json-parse-even-better-errors": "^2.3.0", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dev": true, + "requires": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "dependencies": { + "hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true + }, + "normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "requires": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true + }, + "type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "dev": true + } + } + }, + "read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dev": true, + "requires": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "dependencies": { + "type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "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" + } + }, + "readdir-scoped-modules": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz", + "integrity": "sha512-asaikDeqAQg7JifRsZn1NJZXo9E+VwlyCfbkZhwyISinqk5zNS6266HS5kah6P0SaQKGF6SkNnZVHUzHFYxYDw==", + "dev": true, + "requires": { + "debuglog": "^1.0.1", + "dezalgo": "^1.0.0", + "graceful-fs": "^4.1.2", + "once": "^1.3.0" + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "requires": { + "picomatch": "^2.2.1" + } + }, + "rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", + "dev": true, + "requires": { + "resolve": "^1.1.6" + } + }, + "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==" + }, + "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.15.0", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.15.0.tgz", + "integrity": "sha512-LsrGtPmbYg19bcPHwdtmXwbW+TqNvtY4riE3P83foeHRroMbH6/2ddFBfab3t7kbzc7v7p4wbkIecHImqt0QNg==", + "dev": true, + "requires": { + "@babel/runtime": "^7.8.4" + } + }, + "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-to-ast": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/regexp-to-ast/-/regexp-to-ast-0.5.0.tgz", + "integrity": "sha512-tlbJqcMHnPKI9zSrystikWKwHkBqu2a/Sgw01h3zFjvYrMxEDYHzzoMZnUrbIfpTFEsoRnnviOXNCzFiSc54Qw==", + "dev": true + }, + "regexp.prototype.flags": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" + } + }, + "regexpu-core": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.1.0.tgz", + "integrity": "sha512-bb6hk+xWd2PEOkj5It46A16zFMs2mv86Iwpdu94la4S3sJ7C973h2dHpYKwIBGaWSO7cIRJ+UX0IeMaWcO4qwA==", + "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" + } + }, + "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": "sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==", + "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": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", + "dev": true + }, + "replace-ext": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz", + "integrity": "sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==", + "dev": true + }, + "request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "dev": true, + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "jsprim": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "dev": true, + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.4.0", + "verror": "1.10.0" + } + }, + "qs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", + "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 + } + } + }, + "request-progress": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-3.0.0.tgz", + "integrity": "sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg==", + "dev": true, + "requires": { + "throttleit": "^1.0.0" + } + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==" + }, + "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==", + "dev": true + }, + "requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true + }, + "resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dev": true, + "requires": { + "is-core-module": "^2.9.0", + "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" + } + }, + "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-url-loader": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-5.0.0.tgz", + "integrity": "sha512-uZtduh8/8srhBoMx//5bwqjQ+rfYOUq8zC9NrMUGtjBiGTtFJM42s58/36+hTqeqINcnYe08Nj3LkK9lW4N8Xg==", + "dev": true, + "requires": { + "adjust-sourcemap-loader": "^4.0.0", + "convert-source-map": "^1.7.0", + "loader-utils": "^2.0.0", + "postcss": "^8.2.14", + "source-map": "0.6.1" + }, + "dependencies": { + "loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.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 + } + } + }, + "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 + }, + "resp-modifier": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/resp-modifier/-/resp-modifier-6.0.2.tgz", + "integrity": "sha512-U1+0kWC/+4ncRFYqQWTx/3qkfE6a4B/h3XXgmXypfa0SPZ3t7cbbaFk297PjQS/yov24R18h6OZe6iZwj3NSLw==", + "dev": true, + "requires": { + "debug": "^2.2.0", + "minimatch": "^3.0.2" + }, + "dependencies": { + "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" + } + }, + "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" + } + }, + "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" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true + } + } + }, + "restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "requires": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + } + }, + "ret": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.2.2.tgz", + "integrity": "sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ==", + "dev": true + }, + "retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "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" + }, + "dependencies": { + "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" + } + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "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" + } + } + } + }, + "run-async": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", + "integrity": "sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==", + "dev": true + }, + "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" + } + }, + "rx": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/rx/-/rx-4.1.0.tgz", + "integrity": "sha512-CiaiuN6gapkdl+cZUr67W6I8jquN4lkak3vtIsIWCl4XIPP8ffsoyN6/+PuGXnQy8Cu8W2y9Xxh31Rq4M6wUug==", + "dev": true + }, + "rxjs": { + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.6.tgz", + "integrity": "sha512-dnyv2/YsXhnm461G+R/Pe5bWP41Nm6LBXEYWI6eiFP4fiwx6WRI/CD0zbdVAudd9xwLEF2IDcKXLHit0FYjUzw==", + "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-stable-stringify": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.3.1.tgz", + "integrity": "sha512-kYBSfT+troD9cDA85VDnHZ1rpHC50O0g1e6WlGHVCz/g+JS+9WKLj+XwFYyR8UbrZN8ll9HUpDAAddY58MGisg==", + "dev": true + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "sass": { + "version": "1.54.4", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.54.4.tgz", + "integrity": "sha512-3tmF16yvnBwtlPrNBHw/H907j8MlOX8aTBnlNX1yrKx24RKcJGPyLhFUwkoKBKesR3unP93/2z14Ll8NicwQUA==", + "dev": true, + "requires": { + "chokidar": ">=3.0.0 <4.0.0", + "immutable": "^4.0.0", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "dependencies": { + "immutable": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.1.0.tgz", + "integrity": "sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==", + "dev": true + } + } + }, + "sass-loader": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/sass-loader/-/sass-loader-13.0.2.tgz", + "integrity": "sha512-BbiqbVmbfJaWVeOOAu2o7DhYWtcNmTfvroVgFXa6k2hHheMxNAeDHLNoDy/Q5aoaVlz0LH+MbMktKwm9vN/j8Q==", + "dev": true, + "requires": { + "klona": "^2.0.4", + "neo-async": "^2.6.2" + } + }, + "sax": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", + "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==", + "dev": true + }, + "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 + } + } + }, + "scoped-regex": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/scoped-regex/-/scoped-regex-2.1.0.tgz", + "integrity": "sha512-g3WxHrqSWCZHGHlSrF51VXFdjImhwvH8ZO/pryFH56Qi0cDsZfylQa/t0jCzVQFNbNvM00HfHjkDPEuarKDSWQ==", + "dev": true + }, + "select-hose": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", + "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", + "dev": true + }, + "selfsigned": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.1.1.tgz", + "integrity": "sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ==", + "dev": true, + "requires": { + "node-forge": "^1" + } + }, + "semver": { + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.7.tgz", + "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==", + "requires": { + "lru-cache": "^6.0.0" + }, + "dependencies": { + "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" + } + } + } + }, + "send": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz", + "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==", + "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.6.2", + "mime": "1.4.1", + "ms": "2.0.0", + "on-finished": "~2.3.0", + "range-parser": "~1.2.0", + "statuses": "~1.4.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" + } + }, + "http-errors": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz", + "integrity": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "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": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "dev": true + }, + "mime": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz", + "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "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 + }, + "statuses": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz", + "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==", + "dev": true + } + } + }, + "serialize-javascript": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz", + "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==", + "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": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==", + "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": "sha512-lks+lVC8dgGyh97jxvxeYTWQFvh4uw4yC12gVl63Cg30sjPX4wuGcdkICVXDAESr6OJGjqGA8Iz5mkeN6zlD7A==", + "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": "sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==", + "dev": true + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "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 + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "dev": true + } + } + }, + "serve-static": { + "version": "1.13.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz", + "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==", + "dev": true, + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.2", + "send": "0.16.2" + } + }, + "server-destroy": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/server-destroy/-/server-destroy-1.0.1.tgz", + "integrity": "sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ==", + "dev": true + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "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 + }, + "shell-quote": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz", + "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==", + "dev": true + }, + "shelljs": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", + "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", + "dev": true, + "requires": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + }, + "dependencies": { + "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" + } + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "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" + } + } + } + }, + "shellwords": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", + "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", + "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==", + "dev": true + }, + "simple-git": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.13.0.tgz", + "integrity": "sha512-VYrs3joeHvWGcN3K135RpGpPjm4AHYeOrclwew6LlfHgq6ozQYIW2yMnmjf4PCgVOuSYCbXkdUjyiFawuJz8MA==", + "dev": true, + "requires": { + "@kwsites/file-exists": "^1.1.1", + "@kwsites/promise-deferred": "^1.1.1", + "debug": "^4.3.4" + } + }, + "simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dev": true, + "requires": { + "is-arrayish": "^0.3.1" + }, + "dependencies": { + "is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "dev": true + } + } + }, + "sirv": { + "version": "1.0.19", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-1.0.19.tgz", + "integrity": "sha512-JuLThK3TnZG1TAKDwNIqNq6QA2afLOCcm+iE8D1Kj3GA40pSPsxQjjJl0J8X3tsR7T+CP1GavpzLwYkgVLWrZQ==", + "dev": true, + "requires": { + "@polka/url": "^1.0.0-next.20", + "mrmime": "^1.0.0", + "totalist": "^1.0.0" + } + }, + "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.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.1.1.tgz", + "integrity": "sha512-qDOv24WjnYuL+wbwHdlsYZFy+cgPtrYw0Tn7GLORicQp9BkQLzrgI3Pm4VyR9ERZ41YTn7KlMPuL1n05WdZvmg==", + "dev": true + } + } + }, + "smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true + }, + "socket.io": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.6.1.tgz", + "integrity": "sha512-KMcaAi4l/8+xEjkRICl6ak8ySoxsYG+gG6/XfRCPJPQ/haCRIJBTL4wIl8YCsmtaBovcAXGLOShyVWQ/FG8GZA==", + "dev": true, + "requires": { + "accepts": "~1.3.4", + "base64id": "~2.0.0", + "debug": "~4.3.2", + "engine.io": "~6.4.1", + "socket.io-adapter": "~2.5.2", + "socket.io-parser": "~4.2.1" + } + }, + "socket.io-adapter": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz", + "integrity": "sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==", + "dev": true, + "requires": { + "ws": "~8.11.0" + } + }, + "socket.io-client": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.6.1.tgz", + "integrity": "sha512-5UswCV6hpaRsNg5kkEHVcbBIXEYoVbMQaHJBXJCyEQ+CiFPV1NIOY0XOFWG4XR4GZcB8Kn6AsRs/9cy9TbqVMQ==", + "dev": true, + "requires": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.4.0", + "socket.io-parser": "~4.2.1" + } + }, + "socket.io-parser": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.3.tgz", + "integrity": "sha512-JMafRntWVO2DCJimKsRTh/wnqVvO4hrfwOqtO7f+uzwsQMuxO6VwImtYxaQ+ieoyshWOTJyV0fA21lccEXRPpQ==", + "dev": true, + "requires": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + } + }, + "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" + } + }, + "socks": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.7.0.tgz", + "integrity": "sha512-scnOe9y4VuiNUULJN72GrM26BNOjVsfPXI+j+98PkyEfsIXroa5ofyjT+FzGvn/xHs73U2JtoBYAVx9Hl4quSA==", + "dev": true, + "requires": { + "ip": "^2.0.0", + "smart-buffer": "^4.2.0" + } + }, + "socks-proxy-agent": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", + "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", + "dev": true, + "requires": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + } + }, + "sort-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/sort-keys/-/sort-keys-4.2.0.tgz", + "integrity": "sha512-aUYIEU/UviqPgc8mHR6IW1EGxkAXpeRETYcrzg8cLAvUPZcpAlleSXHV2mY7G12GphSH6Gzv+4MMVSSkbdteHg==", + "dev": true, + "requires": { + "is-plain-obj": "^2.0.0" + } + }, + "sort-object-keys": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sort-object-keys/-/sort-object-keys-1.1.3.tgz", + "integrity": "sha512-855pvK+VkU7PaKYPc+Jjnmt4EzejQHyhhF33q31qG8x7maDzkeFhAAThdCYay11CISO+qAMwjOBP+fPZe0IPyg==", + "dev": true + }, + "sort-package-json": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/sort-package-json/-/sort-package-json-2.4.1.tgz", + "integrity": "sha512-Nd3rgLBJcZ4iw7tpuOhwBupG6SvUDU0Fy1cZGAMorA2JmDUb+29Dg5phJK9gapa2Ak9d15w/RuMl/viwX+nKwQ==", + "dev": true, + "requires": { + "detect-indent": "^7.0.1", + "detect-newline": "^4.0.0", + "git-hooks-list": "^3.0.0", + "globby": "^13.1.2", + "is-plain-obj": "^4.1.0", + "sort-object-keys": "^1.1.3" + }, + "dependencies": { + "detect-newline": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-4.0.0.tgz", + "integrity": "sha512-1aXUEPdfGdzVPFpzGJJNgq9o81bGg1s09uxTWsqBlo9PI332uyJRQq13+LK/UN4JfxJbFdCXonUFQ9R/p7yCtw==", + "dev": true + }, + "globby": { + "version": "13.1.4", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.4.tgz", + "integrity": "sha512-iui/IiiW+QrJ1X1hKH5qwlMQyv34wJAYwH1vrf8b9kBA4sNiif3gKsMHa+BrdnOpEudWjpotfa7LrTzB1ERS/g==", + "dev": true, + "requires": { + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.11", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^4.0.0" + } + }, + "is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "dev": true + }, + "slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "dev": true + } + } + }, + "source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "dev": true + }, + "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 + }, + "source-map-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/source-map-loader/-/source-map-loader-4.0.0.tgz", + "integrity": "sha512-i3KVgM3+QPAHNbGavK+VBq03YoJl24m9JWNbLgsjTj8aJzXG9M61bantBTNBt7CNwY2FYf+RJRYJ3pzalKjIrw==", + "dev": true, + "requires": { + "abab": "^2.0.6", + "iconv-lite": "^0.6.3", + "source-map-js": "^1.0.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-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "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 + } + } + }, + "sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==" + }, + "spawn-command": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2.tgz", + "integrity": "sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==", + "dev": true + }, + "spdx-correct": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", + "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", + "dev": true, + "requires": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-exceptions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", + "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", + "dev": true + }, + "spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "requires": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "spdx-license-ids": { + "version": "3.0.12", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.12.tgz", + "integrity": "sha512-rr+VVSXtRhO4OHbXUiAF7xW3Bo9DuuF6C5jH+q/x15j2jniycgKbxU09Hr0WqlSLUs4i4ltHGXqTe7VHclYWyA==", + "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" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true + }, + "sshpk": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.17.0.tgz", + "integrity": "sha512-/9HIEs1ZXGhSPE8X6Ccm7Nam1z8KcoCqPdI7ecm1N33EzAetWahvQWVqLZtaZQ+IDKX4IyA2o0gBzqIMkAagHQ==", + "dev": true, + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "ssri": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz", + "integrity": "sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==", + "dev": true, + "requires": { + "minipass": "^3.1.1" + } + }, + "stack-trace": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", + "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", + "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 + } + } + }, + "statuses": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.3.1.tgz", + "integrity": "sha512-wuTCPGlJONk/a1kqZ4fQM2+908lC7fa7nPYpTC1EhnvqLX/IICbeP1OZGDtA374trpSq68YubKUMo8oRhN46yg==", + "dev": true + }, + "stream-throttle": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/stream-throttle/-/stream-throttle-0.1.3.tgz", + "integrity": "sha512-889+B9vN9dq7/vLbGyuHeZ6/ctf5sNuGWsDy89uNxkFTAgzy0eK7+w5fL3KLNRTkLle7EgZGvHUphZW0Q26MnQ==", + "dev": true, + "requires": { + "commander": "^2.2.0", + "limiter": "^1.0.5" + }, + "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 + } + } + }, + "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.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz", + "integrity": "sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + } + }, + "string.prototype.trimstart": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz", + "integrity": "sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.19.5" + } + }, + "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-bom-buf": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-buf/-/strip-bom-buf-1.0.0.tgz", + "integrity": "sha512-1sUIL1jck0T1mhOLP2c696BIznzT525Lkub+n4jjMHjhjhoAQA6Ye659DxdlZBr0aLDMQoTxKIpnlqxgtwjsuQ==", + "dev": true, + "requires": { + "is-utf8": "^0.2.1" + } + }, + "strip-bom-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom-stream/-/strip-bom-stream-2.0.0.tgz", + "integrity": "sha512-yH0+mD8oahBZWnY43vxs4pSinn8SMKAdml/EOGBewoe1Y0Eitd0h2Mg3ZRiXruUW6L4P+lvZiEgbh0NgUGia1w==", + "dev": true, + "requires": { + "first-chunk-stream": "^2.0.0", + "strip-bom": "^2.0.0" + }, + "dependencies": { + "strip-bom": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", + "integrity": "sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g==", + "dev": true, + "requires": { + "is-utf8": "^0.2.0" + } + } + } + }, + "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 + }, + "stylus": { + "version": "0.59.0", + "resolved": "https://registry.npmjs.org/stylus/-/stylus-0.59.0.tgz", + "integrity": "sha512-lQ9w/XIOH5ZHVNuNbWW8D822r+/wBSO/d6XvtyHLF7LW4KaCIDeVbvn5DF8fGCJAUCwVhVi/h6J0NUcnylUEjg==", + "dev": true, + "requires": { + "@adobe/css-tools": "^4.0.1", + "debug": "^4.3.2", + "glob": "^7.1.6", + "sax": "~1.2.4", + "source-map": "^0.7.3" + }, + "dependencies": { + "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" + } + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "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" + } + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + } + } + }, + "stylus-loader": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/stylus-loader/-/stylus-loader-7.0.0.tgz", + "integrity": "sha512-WTbtLrNfOfLgzTaR9Lj/BPhQroKk/LC1hfTXSUbrxmxgfUo3Y3LpmKRVA2R1XbjvTAvOfaian9vOyfv1z99E+A==", + "dev": true, + "requires": { + "fast-glob": "^3.2.11", + "klona": "^2.0.5", + "normalize-path": "^3.0.0" + } + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + }, + "supports-hyperlinks": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", + "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", + "dev": true, + "requires": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "has-flag": "^4.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 + }, + "swagger-ui-dist": { + "version": "4.19.0", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-4.19.0.tgz", + "integrity": "sha512-9C9fJGI18gK5AhaU5YRyPY1lXJH4lmWh8h9zFMrJBkYzdRjCbAzYl1ayWPYgwFvag/Luqi3Co599OK/39IS2QQ==", + "dev": true + }, + "symbol-observable": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz", + "integrity": "sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==", + "dev": true + }, + "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 + }, + "synckit": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz", + "integrity": "sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q==", + "dev": true, + "requires": { + "@pkgr/utils": "^2.3.1", + "tslib": "^2.5.0" + } + }, + "tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "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.14.2", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.14.2.tgz", + "integrity": "sha512-oL0rGeM/WFQCUd0y2QrWxYnq7tfSuKBiqTjRPWrRgB46WD/kiwHwF8T23z78H6Q6kGCuuHcPB+KULHRdxvVGQA==", + "dev": true, + "requires": { + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "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.3.8", + "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-5.3.8.tgz", + "integrity": "sha512-WiHL3ElchZMsK27P8uIUh4604IgJyAW47LVXGbEoB21DbQcZ+OuMpGjVYnEUaqcWM6dO8uS2qUbA7LSCWqvsbg==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.17", + "jest-worker": "^27.4.5", + "schema-utils": "^3.1.1", + "serialize-javascript": "^6.0.1", + "terser": "^5.16.8" + }, + "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": {} + }, + "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 + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "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" + } + }, + "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" + } + }, + "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" + } + }, + "terser": { + "version": "5.17.4", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.17.4.tgz", + "integrity": "sha512-jcEKZw6UPrgugz/0Tuk/PVyLAPfMBJf5clnGueo45wTweoV8yh7Q7PEkhkJ5uuUbC7zAxEcG3tqNr1bstkQ8nw==", + "dev": true, + "requires": { + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + } + } + } + }, + "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" + }, + "dependencies": { + "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" + } + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "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" + } + } + } + }, + "text-hex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", + "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", + "dev": true + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "textextensions": { + "version": "5.15.0", + "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-5.15.0.tgz", + "integrity": "sha512-MeqZRHLuaGamUXGuVn2ivtU3LA3mLCCIO5kUGoohTCoGmCBg/+8yPhWVX9WSl9telvVd8erftjFk9Fwb2dD6rw==", + "dev": true + }, + "then-request": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/then-request/-/then-request-6.0.2.tgz", + "integrity": "sha512-3ZBiG7JvP3wbDzA9iNY5zJQcHL4jn/0BWtXIkagfz7QgOL/LqjCEOBQuJNZfu0XYnv5JhKh+cDxCPM4ILrqruA==", + "dev": true, + "requires": { + "@types/concat-stream": "^1.6.0", + "@types/form-data": "0.0.33", + "@types/node": "^8.0.0", + "@types/qs": "^6.2.31", + "caseless": "~0.12.0", + "concat-stream": "^1.6.0", + "form-data": "^2.2.0", + "http-basic": "^8.1.1", + "http-response-object": "^3.0.1", + "promise": "^8.0.0", + "qs": "^6.4.0" + }, + "dependencies": { + "@types/node": { + "version": "8.10.66", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.66.tgz", + "integrity": "sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==", + "dev": true + } + } + }, + "throttleit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz", + "integrity": "sha512-rkTVqu6IjfQ/6+uNuuc3sZek4CEYxTJom3IktzgdSxcZqdARuebbA/f4QmAxMQIxqq9ZLEUkSYqvuk1I6VKq4g==", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", + "dev": true + }, + "thunky": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", + "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==", + "dev": true + }, + "tiny-glob": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/tiny-glob/-/tiny-glob-0.2.9.tgz", + "integrity": "sha512-g/55ssRPUjShh+xkfx9UPDXqhckHEsHr4Vd9zX55oSdGZc/MD0m3sferOkwWtp98bv+kcVfEHtRJgBVJzelrzg==", + "dev": true, + "requires": { + "globalyzer": "0.1.0", + "globrex": "^0.1.2" + } + }, + "tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "dev": true, + "requires": { + "rimraf": "^3.0.0" + } + }, + "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": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==" + }, + "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==", + "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 + }, + "totalist": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-1.1.0.tgz", + "integrity": "sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "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 + }, + "treeverse": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/treeverse/-/treeverse-1.0.4.tgz", + "integrity": "sha512-whw60l7r+8ZU8Tu/Uc2yxtc4ZTZbR/PF3u1IPNKGQ6p8EICLb3Z2lAgoqw9bqYd8IkgnsaOcLzYHFckjqNsf0g==", + "dev": true + }, + "triple-beam": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", + "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==", + "dev": true + }, + "ts-jest": { + "version": "28.0.8", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-28.0.8.tgz", + "integrity": "sha512-5FaG0lXmRPzApix8oFG8RKjAz4ehtm8yMKOTy5HX3fY6W8kmvOrmcY0hKDElW52FJov+clhUbrKAqofnj4mXTg==", + "dev": true, + "requires": { + "bs-logger": "0.x", + "fast-json-stable-stringify": "2.x", + "jest-util": "^28.0.0", + "json5": "^2.2.1", + "lodash.memoize": "4.x", + "make-error": "1.x", + "semver": "7.x", + "yargs-parser": "^21.0.1" + } + }, + "ts-node": { + "version": "10.9.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", + "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "dev": true, + "requires": { + "@cspotcode/source-map-support": "^0.8.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.1", + "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.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "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": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true + } + } + }, + "tslib": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.2.tgz", + "integrity": "sha512-5svOrSA2w3iGFDs1HibEVBGbDrAY82bFQ3HZ3ixB+88nsbsWQoKqDRb5UBYAUPEzbBn6dAp5gRNXglySbx1MlA==" + }, + "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 + } + } + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dev": true, + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", + "dev": 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==", + "dev": true + }, + "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" + } + }, + "typed-assert": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/typed-assert/-/typed-assert-1.0.9.tgz", + "integrity": "sha512-KNNZtayBCtmnNmbo5mG47p1XsCyrx6iVqomjcZnec/1Y5GGARaxPs6r49RnSPeUP3YjNYiU9sQHAtY4BBvnZwg==", + "dev": true + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "dev": true + }, + "typescript": { + "version": "4.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.2.tgz", + "integrity": "sha512-C0I1UsrrDHo2fYI5oaCGbSejwX4ch+9Y5jTQELvovfmFkK3HHSZJB8MSJcWLmCUBzQBchCrZ9rMRV6GuNrvGtw==" + }, + "ua-parser-js": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.34.tgz", + "integrity": "sha512-K9mwJm/DaB6mRLZfw6q8IMXipcrmuT6yfhYmwhAkuh+81sChuYstYA+znlgaflUPaYUa3odxKPKGw6Vw/lANew==", + "dev": true + }, + "unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "requires": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + } + }, + "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 + }, + "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" + } + }, + "universal-user-agent": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", + "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==", + "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==", + "dev": true + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true + }, + "untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "dev": true + }, + "update-browserslist-db": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.8.tgz", + "integrity": "sha512-GHg7C4M7oJSJYW/ED/5QOJ7nL/E0lwTOBGsOorA7jqHr8ExUhPfwAotIAmdSw/LWv3SMLSNpzTAgeLG9zaZKTA==", + "requires": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + } + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "url": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", + "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==", + "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": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==", + "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==", + "dev": true, + "requires": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "util": { + "version": "0.12.4", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.4.tgz", + "integrity": "sha512-bxZ9qtSlGUWSOy9Qa9Xgk11kSslpuZwaxCg4sNIDj6FLucDab2JxnHwyNTCpHMtK1MjoQiWQ6DiUMZYbSrO+Sw==", + "dev": true, + "requires": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "safe-buffer": "^5.1.2", + "which-typed-array": "^1.1.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "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-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "v8-to-istanbul": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz", + "integrity": "sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^1.6.0" + } + }, + "validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "requires": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "validate-npm-package-name": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-4.0.0.tgz", + "integrity": "sha512-mzR0L8ZDktZjpX4OB46KT+56MAhl4EIazWP/+G/HPGuvfdaqg4YsCdtOm6U9+LOFyYDoh4dpnpxZRB9MQQns5Q==", + "dev": true, + "requires": { + "builtins": "^5.0.0" + } + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "dev": true, + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "vinyl": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.1.tgz", + "integrity": "sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw==", + "dev": true, + "requires": { + "clone": "^2.1.1", + "clone-buffer": "^1.0.0", + "clone-stats": "^1.0.0", + "cloneable-readable": "^1.0.0", + "remove-trailing-separator": "^1.0.1", + "replace-ext": "^1.0.0" + }, + "dependencies": { + "clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "dev": true + } + } + }, + "vinyl-file": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/vinyl-file/-/vinyl-file-3.0.0.tgz", + "integrity": "sha512-BoJDj+ca3D9xOuPEM6RWVtWQtvEPQiQYn82LvdxhLWplfQsBzBqtgK0yhCP0s1BNTi6dH9BO+dzybvyQIacifg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "pify": "^2.3.0", + "strip-bom-buf": "^1.0.0", + "strip-bom-stream": "^2.0.0", + "vinyl": "^2.0.1" + } + }, + "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": "3.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-3.0.0.tgz", + "integrity": "sha512-3WFqGEgSXIyGhOmAFtlicJNMjEps8b1MG31NCA0/vOF9+nKMUW1ckhi9cnNHmf88Rzw5V+dwIwsm2C7X8k9aQg==", + "dev": true, + "requires": { + "xml-name-validator": "^4.0.0" + } + }, + "wait-on": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/wait-on/-/wait-on-7.0.1.tgz", + "integrity": "sha512-9AnJE9qTjRQOlTZIldAaf/da2eW0eSRSgcqq85mXQja/DW3MriHxkpODDSUEg+Gri/rKEcXUZHe+cevvYItaog==", + "dev": true, + "requires": { + "axios": "^0.27.2", + "joi": "^17.7.0", + "lodash": "^4.17.21", + "minimist": "^1.2.7", + "rxjs": "^7.8.0" + }, + "dependencies": { + "rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "requires": { + "tslib": "^2.1.0" + } + } + } + }, + "walk-up-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/walk-up-path/-/walk-up-path-1.0.0.tgz", + "integrity": "sha512-hwj/qMDUEjCU5h0xr90KGCf0tg0/LgJbmOWgrWKYlcJZM7XvquvUJZ0G/HMGr7F7OQMOUuPHWP9JpriinkAlkg==", + "dev": true + }, + "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.4.0", + "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", + "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", + "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": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "requires": { + "defaults": "^1.0.3" + } + }, + "webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true + }, + "webpack": { + "version": "5.74.0", + "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.74.0.tgz", + "integrity": "sha512-A2InDwnhhGN4LYctJj6M1JEaGL7Luj6LOmyBHjcI8529cm5p6VXiTIW2sn6ffvEAKmveLzvu4jrihwXtPojlAA==", + "dev": 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.7.1", + "acorn-import-assertions": "^1.7.6", + "browserslist": "^4.14.5", + "chrome-trace-event": "^1.0.2", + "enhanced-resolve": "^5.10.0", + "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-even-better-errors": "^2.3.1", + "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.4.0", + "webpack-sources": "^3.2.3" + }, + "dependencies": { + "@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 + }, + "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": {} + }, + "events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "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-bundle-analyzer": { + "version": "4.6.1", + "resolved": "https://registry.npmjs.org/webpack-bundle-analyzer/-/webpack-bundle-analyzer-4.6.1.tgz", + "integrity": "sha512-oKz9Oz9j3rUciLNfpGFjOb49/jEpXNmWdVH8Ls//zNcnLlQdTGXQQMsBbb/gR7Zl8WNLxVCq+0Hqbx3zv6twBw==", + "dev": true, + "requires": { + "acorn": "^8.0.4", + "acorn-walk": "^8.0.0", + "chalk": "^4.1.0", + "commander": "^7.2.0", + "gzip-size": "^6.0.0", + "lodash": "^4.17.20", + "opener": "^1.5.2", + "sirv": "^1.0.7", + "ws": "^7.3.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 + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.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==", + "dev": true, + "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==", + "dev": true + }, + "commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "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==", + "dev": true + }, + "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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "ws": { + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "dev": true, + "requires": {} + } + } + }, + "webpack-dev-middleware": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz", + "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==", + "dev": true, + "requires": { + "colorette": "^2.0.10", + "memfs": "^3.4.3", + "mime-types": "^2.1.31", + "range-parser": "^1.2.1", + "schema-utils": "^4.0.0" + }, + "dependencies": { + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } + } + } + }, + "webpack-dev-server": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.10.0.tgz", + "integrity": "sha512-7dezwAs+k6yXVFZ+MaL8VnE+APobiO3zvpp3rBHe/HmWQ+avwh0Q3d0xxacOiBybZZ3syTZw9HXzpa3YNbAZDQ==", + "dev": true, + "requires": { + "@types/bonjour": "^3.5.9", + "@types/connect-history-api-fallback": "^1.3.5", + "@types/express": "^4.17.13", + "@types/serve-index": "^1.9.1", + "@types/serve-static": "^1.13.10", + "@types/sockjs": "^0.3.33", + "@types/ws": "^8.5.1", + "ansi-html-community": "^0.0.8", + "bonjour-service": "^1.0.11", + "chokidar": "^3.5.3", + "colorette": "^2.0.10", + "compression": "^1.7.4", + "connect-history-api-fallback": "^2.0.0", + "default-gateway": "^6.0.3", + "express": "^4.17.3", + "graceful-fs": "^4.2.6", + "html-entities": "^2.3.2", + "http-proxy-middleware": "^2.0.3", + "ipaddr.js": "^2.0.1", + "open": "^8.0.9", + "p-retry": "^4.5.0", + "rimraf": "^3.0.2", + "schema-utils": "^4.0.0", + "selfsigned": "^2.0.1", + "serve-index": "^1.9.1", + "sockjs": "^0.3.24", + "spdy": "^4.0.2", + "webpack-dev-middleware": "^5.3.1", + "ws": "^8.4.2" + }, + "dependencies": { + "connect-history-api-fallback": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz", + "integrity": "sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==", + "dev": true + }, + "schema-utils": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", + "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.8.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.0.0" + } + } + } + }, + "webpack-merge": { + "version": "5.9.0", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.9.0.tgz", + "integrity": "sha512-6NbRQw4+Sy50vYNTw7EyOn41OZItPiXB8GNv3INSoe3PSFaHJEz3SHTrYVaRm2LilNGnFUzh0FAwqPEmU/CwDg==", + "dev": true, + "requires": { + "clone-deep": "^4.0.1", + "wildcard": "^2.0.0" + } + }, + "webpack-notifier": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/webpack-notifier/-/webpack-notifier-1.15.0.tgz", + "integrity": "sha512-N2V8UMgRB5komdXQRavBsRpw0hPhJq2/SWNOGuhrXpIgRhcMexzkGQysUyGStHLV5hkUlgpRiF7IUXoBqyMmzQ==", + "dev": true, + "requires": { + "node-notifier": "^9.0.0", + "strip-ansi": "^6.0.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 + }, + "webpack-subresource-integrity": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/webpack-subresource-integrity/-/webpack-subresource-integrity-5.1.0.tgz", + "integrity": "sha512-sacXoX+xd8r4WKsy9MvH/q/vBtEHr86cpImXwyg74pFIpERKt6FmB8cXpeuh0ZLgclOlHI4Wcll7+R5L02xk9Q==", + "dev": true, + "requires": { + "typed-assert": "^1.0.8" + } + }, + "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": "2.0.0", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", + "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "dev": true, + "requires": { + "iconv-lite": "0.6.3" + }, + "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" + } + } + } + }, + "whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "dev": true + }, + "whatwg-url": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-10.0.0.tgz", + "integrity": "sha512-CLxxCmdUby142H5FZzn4D8ikO1cmypvXVQktsgosNy4a4BHrDHeciBBGZhb0bNoR5/MltoCatso+vFjjGx8t0w==", + "dev": true, + "requires": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.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-pm": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-pm/-/which-pm-2.0.0.tgz", + "integrity": "sha512-Lhs9Pmyph0p5n5Z3mVnN0yWcbQYUAD7rbQUiMsQxOJ3T57k7RFe35SUwWMf7dsbDZks1uOmw4AecB/JMDj3v/w==", + "dev": true, + "requires": { + "load-yaml-file": "^0.2.0", + "path-exists": "^4.0.0" + } + }, + "which-typed-array": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.8.tgz", + "integrity": "sha512-Jn4e5PItbcAHyLoRDwvPj1ypu27DJbtdYXUa5zsinrUx77Uvfb0cXwwnGMTn7cjUfhhqgVQnVJCwF+7cgU7tpw==", + "dev": true, + "requires": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-abstract": "^1.20.0", + "for-each": "^0.3.3", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.9" + } + }, + "wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dev": true, + "requires": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "wildcard": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", + "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", + "dev": true + }, + "windows-release": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/windows-release/-/windows-release-4.0.0.tgz", + "integrity": "sha512-OxmV4wzDKB1x7AZaZgXMVsdJ1qER1ed83ZrTYd5Bwq2HfJVg3DJS8nqlAG4sMoJ7mu8cuRmLEYyU13BKwctRAg==", + "dev": true, + "requires": { + "execa": "^4.0.2" + } + }, + "winston": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/winston/-/winston-3.8.1.tgz", + "integrity": "sha512-r+6YAiCR4uI3N8eQNOg8k3P3PqwAm20cLKlzVD9E66Ch39+LZC+VH1UKf9JemQj2B3QoUHfKD7Poewn0Pr3Y1w==", + "dev": true, + "requires": { + "@dabh/diagnostics": "^2.0.2", + "async": "^3.2.3", + "is-stream": "^2.0.0", + "logform": "^2.4.0", + "one-time": "^1.0.0", + "readable-stream": "^3.4.0", + "safe-stable-stringify": "^2.3.1", + "stack-trace": "0.0.x", + "triple-beam": "^1.3.0", + "winston-transport": "^4.5.0" + } + }, + "winston-transport": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.5.0.tgz", + "integrity": "sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q==", + "dev": true, + "requires": { + "logform": "^2.3.2", + "readable-stream": "^3.6.0", + "triple-beam": "^1.3.0" + } + }, + "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==", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "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" + } + }, + "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==" + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "dev": true, + "requires": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + } + }, + "ws": { + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "dev": true, + "requires": {} + }, + "xml": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", + "integrity": "sha512-huCv9IH9Tcf95zuYCsQraZtWnJvBtLVE0QHMOs8bWyZAFZNDcYjsPq1nEx8jKA9y+Beo9v+7OBPRisQTjinQMw==", + "dev": true + }, + "xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "dev": true + }, + "xml2js": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", + "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", + "dev": true, + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~9.0.1" + } + }, + "xmlbuilder": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", + "integrity": "sha512-7YXTQc3P2l9+0rjaUbLwMKRhtmwg1M1eDf6nag7urC7pIPYLD9W/jmzQ4ptRSUbodw5S0jfoGTflLemQibSpeQ==", + "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 + }, + "xmlhttprequest-ssl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", + "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", + "dev": true + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "yaml": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz", + "integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==", + "dev": true + }, + "yargs": { + "version": "17.5.1", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.5.1.tgz", + "integrity": "sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==", + "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" + } + }, + "yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==" + }, + "yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "requires": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "yeoman-environment": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/yeoman-environment/-/yeoman-environment-3.10.0.tgz", + "integrity": "sha512-sYtSxBK9daq21QjoskJTHKLQ1xEsRPURkmFV/aM8HS8ZlQVzwx57Rz1zCs8EGPhK4vqsmTE8H92Gp1jg1fT3EA==", + "dev": true, + "requires": { + "@npmcli/arborist": "^4.0.4", + "are-we-there-yet": "^2.0.0", + "arrify": "^2.0.1", + "binaryextensions": "^4.15.0", + "chalk": "^4.1.0", + "cli-table": "^0.3.1", + "commander": "7.1.0", + "dateformat": "^4.5.0", + "debug": "^4.1.1", + "diff": "^5.0.0", + "error": "^10.4.0", + "escape-string-regexp": "^4.0.0", + "execa": "^5.0.0", + "find-up": "^5.0.0", + "globby": "^11.0.1", + "grouped-queue": "^2.0.0", + "inquirer": "^8.0.0", + "is-scoped": "^2.1.0", + "isbinaryfile": "^4.0.10", + "lodash": "^4.17.10", + "log-symbols": "^4.0.0", + "mem-fs": "^1.2.0 || ^2.0.0", + "mem-fs-editor": "^8.1.2 || ^9.0.0", + "minimatch": "^3.0.4", + "npmlog": "^5.0.1", + "p-queue": "^6.6.2", + "p-transform": "^1.3.0", + "pacote": "^12.0.2", + "preferred-pm": "^3.0.3", + "pretty-bytes": "^5.3.0", + "semver": "^7.1.3", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0", + "text-table": "^0.2.0", + "textextensions": "^5.12.0", + "untildify": "^4.0.0" + }, + "dependencies": { + "@npmcli/fs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", + "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", + "dev": true, + "requires": { + "@gar/promisify": "^1.0.1", + "semver": "^7.3.5" + } + }, + "@npmcli/git": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@npmcli/git/-/git-2.1.0.tgz", + "integrity": "sha512-/hBFX/QG1b+N7PZBFs0bi+evgRZcK9nWBxQKZkGoXUT5hJSwl5c4d7y8/hm+NQZRPhQ67RzFaj5UM9YeyKoryw==", + "dev": true, + "requires": { + "@npmcli/promise-spawn": "^1.3.2", + "lru-cache": "^6.0.0", + "mkdirp": "^1.0.4", + "npm-pick-manifest": "^6.1.1", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^2.0.2" + } + }, + "@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" + } + }, + "@npmcli/node-gyp": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@npmcli/node-gyp/-/node-gyp-1.0.3.tgz", + "integrity": "sha512-fnkhw+fmX65kiLqk6E3BFLXNC26rUhK90zVwe2yncPliVT/Qos3xjhTLE59Df8KnPlcwIERXKVlU1bXoUQ+liA==", + "dev": true + }, + "@npmcli/promise-spawn": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@npmcli/promise-spawn/-/promise-spawn-1.3.2.tgz", + "integrity": "sha512-QyAGYo/Fbj4MXeGdJcFzZ+FkDkomfRBrPM+9QYJSg+PxgAUL+LU3FneQk37rKR2/zjqkCV1BLHccX98wRXG3Sg==", + "dev": true, + "requires": { + "infer-owner": "^1.0.4" + } + }, + "@npmcli/run-script": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/run-script/-/run-script-2.0.0.tgz", + "integrity": "sha512-fSan/Pu11xS/TdaTpTB0MRn9guwGU8dye+x56mEVgBEd/QsybBbYcAL0phPXi8SGWFEChkQd6M9qL4y6VOpFig==", + "dev": true, + "requires": { + "@npmcli/node-gyp": "^1.0.2", + "@npmcli/promise-spawn": "^1.3.2", + "node-gyp": "^8.2.0", + "read-package-json-fast": "^2.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 + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "dev": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + } + }, + "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" + } + }, + "builtins": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/builtins/-/builtins-1.0.3.tgz", + "integrity": "sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==", + "dev": true + }, + "cacache": { + "version": "15.3.0", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", + "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", + "dev": true, + "requires": { + "@npmcli/fs": "^1.0.0", + "@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" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.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==", + "dev": true, + "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==", + "dev": true + }, + "commander": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.1.0.tgz", + "integrity": "sha512-pRxBna3MJe6HKnBGsDyMv8ETbptw3axEdYHoqNh7gu5oDcew8fs0xnivZGm06Ogk8zGAJ9VX+OPEr2GXEQK4dg==", + "dev": true + }, + "diff": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.1.0.tgz", + "integrity": "sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw==", + "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==", + "dev": true + }, + "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" + } + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "dev": true, + "requires": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + } + }, + "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 + }, + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "requires": { + "lru-cache": "^6.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" + } + }, + "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 + }, + "ignore-walk": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-4.0.1.tgz", + "integrity": "sha512-rzDQLaW4jQbh2YrOFlJdCtX8qgJTehFRYiUB2r1osqTeDzV/3+Jh8fz1oAPzUThf3iku8Ds4IDqawI5d8mUiQw==", + "dev": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "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==", + "dev": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "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" + } + }, + "minipass-fetch": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", + "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", + "dev": true, + "requires": { + "encoding": "^0.1.12", + "minipass": "^3.1.0", + "minipass-sized": "^1.0.3", + "minizlib": "^2.0.0" + } + }, + "node-gyp": { + "version": "8.4.1", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", + "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", + "dev": true, + "requires": { + "env-paths": "^2.2.0", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^9.1.0", + "nopt": "^5.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "dependencies": { + "are-we-there-yet": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", + "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", + "dev": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + } + }, + "gauge": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", + "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "dev": true, + "requires": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + } + }, + "make-fetch-happen": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", + "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", + "dev": true, + "requires": { + "agentkeepalive": "^4.1.3", + "cacache": "^15.2.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^6.0.0", + "minipass": "^3.1.3", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^1.3.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.2", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^6.0.0", + "ssri": "^8.0.0" + } + }, + "npmlog": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", + "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", + "dev": true, + "requires": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + } + } + } + }, + "npm-install-checks": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-4.0.0.tgz", + "integrity": "sha512-09OmyDkNLYwqKPOnbI8exiOZU2GVVmQp7tgez2BPi5OZC8M82elDAps7sxC4l//uSUtotWqoEIDwjRvWH4qz8w==", + "dev": true, + "requires": { + "semver": "^7.1.1" + } + }, + "npm-package-arg": { + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/npm-package-arg/-/npm-package-arg-8.1.5.tgz", + "integrity": "sha512-LhgZrg0n0VgvzVdSm1oiZworPbTxYHUJCgtsJW8mGvlDpxTM1vSJc3m5QZeUkhAHIzbz3VCHd/R4osi1L1Tg/Q==", + "dev": true, + "requires": { + "hosted-git-info": "^4.0.1", + "semver": "^7.3.4", + "validate-npm-package-name": "^3.0.0" + } + }, + "npm-packlist": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-3.0.0.tgz", + "integrity": "sha512-L/cbzmutAwII5glUcf2DBRNY/d0TFd4e/FnaZigJV6JD85RHZXJFGwCndjMWiiViiWSsWt3tiOLpI3ByTnIdFQ==", + "dev": true, + "requires": { + "glob": "^7.1.6", + "ignore-walk": "^4.0.1", + "npm-bundled": "^1.1.1", + "npm-normalize-package-bin": "^1.0.1" + } + }, + "npm-pick-manifest": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/npm-pick-manifest/-/npm-pick-manifest-6.1.1.tgz", + "integrity": "sha512-dBsdBtORT84S8V8UTad1WlUyKIY9iMsAmqxHbLdeEeBNMLQDlDWWra3wYUx9EBEIiG/YwAy0XyNHDd2goAsfuA==", + "dev": true, + "requires": { + "npm-install-checks": "^4.0.0", + "npm-normalize-package-bin": "^1.0.1", + "npm-package-arg": "^8.1.2", + "semver": "^7.3.4" + } + }, + "npm-registry-fetch": { + "version": "12.0.2", + "resolved": "https://registry.npmjs.org/npm-registry-fetch/-/npm-registry-fetch-12.0.2.tgz", + "integrity": "sha512-Df5QT3RaJnXYuOwtXBXS9BWs+tHH2olvkCLh6jcR/b/u3DvPMlp3J0TvvYwplPKxHMOwfg287PYih9QqaVFoKA==", + "dev": true, + "requires": { + "make-fetch-happen": "^10.0.1", + "minipass": "^3.1.6", + "minipass-fetch": "^1.4.1", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.1.2", + "npm-package-arg": "^8.1.5" + } + }, + "npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "dev": true, + "requires": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "pacote": { + "version": "12.0.3", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-12.0.3.tgz", + "integrity": "sha512-CdYEl03JDrRO3x18uHjBYA9TyoW8gy+ThVcypcDkxPtKlw76e4ejhYB6i9lJ+/cebbjpqPW/CijjqxwDTts8Ow==", + "dev": true, + "requires": { + "@npmcli/git": "^2.1.0", + "@npmcli/installed-package-contents": "^1.0.6", + "@npmcli/promise-spawn": "^1.2.0", + "@npmcli/run-script": "^2.0.0", + "cacache": "^15.0.5", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "infer-owner": "^1.0.4", + "minipass": "^3.1.3", + "mkdirp": "^1.0.3", + "npm-package-arg": "^8.0.1", + "npm-packlist": "^3.0.0", + "npm-pick-manifest": "^6.0.0", + "npm-registry-fetch": "^12.0.0", + "promise-retry": "^2.0.1", + "read-package-json-fast": "^2.0.1", + "rimraf": "^3.0.2", + "ssri": "^8.0.1", + "tar": "^6.1.0" + } + }, + "socks-proxy-agent": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz", + "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==", + "dev": true, + "requires": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + } + }, + "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" + } + }, + "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==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "validate-npm-package-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz", + "integrity": "sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw==", + "dev": true, + "requires": { + "builtins": "^1.0.3" + } + } + } + }, + "yeoman-generator": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/yeoman-generator/-/yeoman-generator-5.7.0.tgz", + "integrity": "sha512-z9ZwgKoDOd+llPDCwn8Ax2l4In5FMhlslxdeByW4AMxhT+HbTExXKEAahsClHSbwZz1i5OzRwLwRIUdOJBr5Bw==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "dargs": "^7.0.0", + "debug": "^4.1.1", + "execa": "^5.1.1", + "github-username": "^6.0.0", + "lodash": "^4.17.11", + "minimist": "^1.2.5", + "read-pkg-up": "^7.0.1", + "run-async": "^2.0.0", + "semver": "^7.2.1", + "shelljs": "^0.8.5", + "sort-keys": "^4.2.0", + "text-table": "^0.2.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.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==", + "dev": true, + "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==", + "dev": true + }, + "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" + } + }, + "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 + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "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 + }, + "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==", + "dev": true, + "requires": { + "has-flag": "^4.0.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.6", + "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.11.6.tgz", + "integrity": "sha512-umJqFtKyZlPli669gB1gOrRE9hxUUGkZr7mo878z+NEBJZZixJkKeVYfnoLa7g25SseUDc92OZrMKKHySyJrFg==", + "requires": { + "tslib": "^2.3.0" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 00000000..4ff4c27c --- /dev/null +++ b/package.json @@ -0,0 +1,164 @@ +{ + "name": "twenty-one-points", + "version": "0.0.1-SNAPSHOT", + "private": true, + "description": "Description for TwentyOnePoints", + "license": "UNLICENSED", + "scripts": { + "app:start": "./gradlew", + "backend:build-cache": "npm run backend:info && npm run backend:nohttp:test && npm run ci:e2e:package", + "backend:doc:test": "./gradlew javadoc -x webapp -x webapp_test", + "backend:info": "./gradlew -v", + "backend:nohttp:test": "./gradlew checkstyleNohttp -x webapp -x webapp_test", + "backend:start": "./gradlew -x webapp -x webapp_test", + "backend:unit:test": "./gradlew test integrationTest -x webapp -x webapp_test -Dlogging.level.ROOT=OFF -Dlogging.level.org.zalando=OFF -Dlogging.level.tech.jhipster=OFF -Dlogging.level.org.jhipster.health=OFF -Dlogging.level.org.springframework=OFF -Dlogging.level.org.springframework.web=OFF -Dlogging.level.org.springframework.security=OFF", + "build": "npm run webapp:prod --", + "build-watch": "concurrently 'npm run webapp:build:dev -- --watch' npm:backend:start", + "ci:backend:test": "npm run backend:info && npm run backend:doc:test && npm run backend:nohttp:test && npm run backend:unit:test -- -P$npm_package_config_default_environment", + "ci:e2e:package": "npm run java:$npm_package_config_packaging:$npm_package_config_default_environment -- -Pe2e -Denforcer.skip=true", + "postci:e2e:package": "cp build/libs/*.$npm_package_config_packaging e2e.$npm_package_config_packaging", + "ci:e2e:prepare": "npm run ci:e2e:prepare:docker", + "ci:e2e:prepare:docker": "npm run docker:db:up && npm run docker:others:up && docker ps -a", + "ci:e2e:run": "concurrently -k -s first \"npm run ci:e2e:server:start\" \"npm run e2e:headless\"", + "preci:e2e:server:start": "npm run docker:db:await --if-present && npm run docker:others:await --if-present", + "ci:e2e:server:start": "java -jar e2e.$npm_package_config_packaging --spring.profiles.active=e2e,$npm_package_config_default_environment -Dlogging.level.ROOT=OFF -Dlogging.level.org.zalando=OFF -Dlogging.level.tech.jhipster=OFF -Dlogging.level.org.jhipster.health=OFF -Dlogging.level.org.springframework=OFF -Dlogging.level.org.springframework.web=OFF -Dlogging.level.org.springframework.security=OFF --logging.level.org.springframework.web=ERROR", + "ci:e2e:teardown": "npm run ci:e2e:teardown:docker", + "ci:e2e:teardown:docker": "npm run docker:db:down --if-present && npm run docker:others:down && docker ps -a", + "ci:frontend:build": "npm run webapp:build:$npm_package_config_default_environment", + "ci:frontend:test": "npm run ci:frontend:build && npm test", + "ci:server:await": "echo \"Waiting for server at port $npm_package_config_backend_port to start\" && wait-on -t 180000 http-get://localhost:$npm_package_config_backend_port/management/health && echo \"Server at port $npm_package_config_backend_port started\"", + "clean-www": "rimraf build/resources/main/static/app/{src,build/}", + "cleanup": "rimraf build/resources/main/static/", + "cypress": "cypress open --e2e", + "docker:app:up": "docker-compose -f src/main/docker/app.yml up -d", + "docker:db:down": "docker-compose -f src/main/docker/postgresql.yml down -v", + "docker:db:up": "docker-compose -f src/main/docker/postgresql.yml up -d", + "docker:elasticsearch:down": "docker-compose -f src/main/docker/elasticsearch.yml down -v", + "docker:elasticsearch:up": "docker-compose -f src/main/docker/elasticsearch.yml up -d", + "docker:others:await": "echo \"Waiting for Elasticsearch to start\" && wait-on -t 180000 \"http-get://localhost:9200/_cluster/health?wait_for_status=green&timeout=60s\" && echo \"Elasticsearch started\"", + "docker:others:down": "npm run docker:elasticsearch:down", + "predocker:others:up": "", + "docker:others:up": "npm run docker:elasticsearch:up", + "e2e": "npm run e2e:cypress:headed --", + "e2e:cypress": "cypress run --e2e --browser chrome --record ${CYPRESS_ENABLE_RECORD:-false}", + "e2e:cypress:headed": "npm run e2e:cypress -- --headed", + "e2e:dev": "concurrently -k -s first \"./gradlew\" \"npm run e2e\"", + "e2e:devserver": "concurrently -k -s first \"npm run backend:start\" \"npm start\" \"wait-on -t 180000 http-get://localhost:9000 && npm run e2e:headless -- -c baseUrl=http://localhost:9000\"", + "pree2e:headless": "npm run ci:server:await", + "e2e:headless": "npm run e2e:cypress --", + "java:docker": "./gradlew bootJar -Pprod jibDockerBuild", + "java:docker:arm64": "npm run java:docker -- -PjibArchitecture=arm64", + "java:docker:dev": "npm run java:docker -- -Pdev,webapp", + "java:docker:prod": "npm run java:docker -- -Pprod", + "java:jar": "./gradlew bootJar -x test -x integrationTest", + "java:jar:dev": "npm run java:jar -- -Pdev,webapp", + "java:jar:prod": "npm run java:jar -- -Pprod", + "java:war": "./gradlew bootWar -Pwar -x test -x integrationTest", + "java:war:dev": "npm run java:war -- -Pdev,webapp", + "java:war:prod": "npm run java:war -- -Pprod", + "jest": "jest --coverage --logHeapUsage --maxWorkers=2 --config jest.conf.js", + "lint": "eslint . --ext .js,.ts", + "lint:fix": "npm run lint -- --fix", + "prepare": "husky install", + "prettier:check": "prettier --check \"{,src/**/,webpack/,.blueprint/**/}*.{md,json,yml,html,cjs,mjs,js,ts,tsx,css,scss,java}\"", + "prettier:format": "prettier --write \"{,src/**/,webpack/,.blueprint/**/}*.{md,json,yml,html,cjs,mjs,js,ts,tsx,css,scss,java}\"", + "serve": "npm run start --", + "start": "ng serve --hmr", + "start-tls": "npm run webapp:dev-ssl", + "pretest": "npm run lint", + "test": "ng test --coverage --log-heap-usage -w=2", + "test:watch": "npm run test -- --watch", + "watch": "concurrently npm:start npm:backend:start", + "webapp:build": "npm run clean-www && npm run webapp:build:dev", + "webapp:build:dev": "ng build --configuration development", + "webapp:build:prod": "ng build --configuration production", + "webapp:dev": "ng serve", + "webapp:dev-ssl": "ng serve --ssl", + "webapp:dev-verbose": "ng serve --verbose", + "webapp:prod": "npm run clean-www && npm run webapp:build:prod", + "webapp:test": "npm run test --" + }, + "config": { + "backend_port": "8080", + "default_environment": "prod", + "packaging": "jar" + }, + "dependencies": { + "@angular/common": "14.2.0", + "@angular/compiler": "14.2.0", + "@angular/core": "14.2.0", + "@angular/forms": "14.2.0", + "@angular/localize": "14.2.0", + "@angular/platform-browser": "14.2.0", + "@angular/platform-browser-dynamic": "14.2.0", + "@angular/router": "14.2.0", + "@fortawesome/angular-fontawesome": "0.11.1", + "@fortawesome/fontawesome-svg-core": "6.2.0", + "@fortawesome/free-solid-svg-icons": "6.2.0", + "@ng-bootstrap/ng-bootstrap": "13.0.0", + "@ngx-translate/core": "14.0.0", + "@ngx-translate/http-loader": "7.0.0", + "@popperjs/core": "2.11.6", + "bootstrap": "5.2.0", + "dayjs": "1.11.5", + "ngx-infinite-scroll": "14.0.0", + "ngx-webstorage": "10.0.1", + "rxjs": "7.5.6", + "tslib": "2.4.0", + "zone.js": "0.11.6" + }, + "devDependencies": { + "@angular-builders/custom-webpack": "14.0.1", + "@angular-builders/jest": "14.0.1", + "@angular-devkit/build-angular": "14.2.1", + "@angular-eslint/eslint-plugin": "14.0.3", + "@angular/cli": "14.2.1", + "@angular/compiler-cli": "14.2.0", + "@angular/service-worker": "14.2.0", + "@types/jest": "28.1.8", + "@types/node": "16.11.56", + "@typescript-eslint/eslint-plugin": "5.36.1", + "@typescript-eslint/parser": "5.36.1", + "browser-sync": "2.27.10", + "browser-sync-webpack-plugin": "2.3.0", + "concurrently": "7.3.0", + "copy-webpack-plugin": "11.0.0", + "cypress": "10.7.0", + "eslint": "8.23.0", + "eslint-config-prettier": "8.5.0", + "eslint-plugin-cypress": "2.12.1", + "eslint-webpack-plugin": "3.2.0", + "folder-hash": "4.0.2", + "generator-jhipster": "7.9.3", + "generator-jhipster-es-entity-reindexer": "1.0.4", + "husky": "7.0.4", + "jest": "28.1.3", + "jest-date-mock": "1.0.8", + "jest-environment-jsdom": "28.1.3", + "jest-junit": "14.0.1", + "jest-preset-angular": "12.2.2", + "jest-sonar": "0.2.12", + "lint-staged": "13.0.3", + "merge-jsons-webpack-plugin": "2.0.1", + "prettier": "2.7.1", + "prettier-plugin-java": "1.6.2", + "prettier-plugin-packagejson": "2.2.18", + "rimraf": "3.0.2", + "swagger-ui-dist": "4.14.0", + "ts-jest": "28.0.8", + "typescript": "4.8.2", + "wait-on": "6.0.1", + "webpack-bundle-analyzer": "4.6.1", + "webpack-merge": "5.8.0", + "webpack-notifier": "1.15.0" + }, + "engines": { + "node": ">=16.17.0" + }, + "cacheDirectories": [ + "node_modules" + ], + "overrides": { + "webpack": "5.74.0" + } +} diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 00000000..63b3b73c --- /dev/null +++ b/settings.gradle @@ -0,0 +1,22 @@ + +pluginManagement { + repositories { + mavenCentral() + maven { url 'https://repo.spring.io/milestone' } + gradlePluginPortal() + //jhipster-needle-gradle-plugin-management-repositories - JHipster will add additional entries here + } + plugins { + id 'org.springframework.boot' version "${springBootVersion}" + id 'com.google.cloud.tools.jib' version "${jibPluginVersion}" + id 'com.gorylenko.gradle-git-properties' version "${gitPropertiesPluginVersion}" + id 'com.github.node-gradle.node' version "${gradleNodePluginVersion}" + id 'org.liquibase.gradle' version "${liquibasePluginVersion}" + id 'org.sonarqube' version "${sonarqubePluginVersion}" + id "io.spring.nohttp" version "${noHttpCheckstyleVersion}" + id 'com.github.andygoossens.gradle-modernizer-plugin' version "${modernizerPluginVersion}" + //jhipster-needle-gradle-plugin-management-plugins - JHipster will add additional entries here + } +} + +rootProject.name = "twenty-one-points" diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 00000000..300efd53 --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1,34 @@ +sonar.projectKey=TwentyOnePoints +sonar.projectName=TwentyOnePoints generated by jhipster + +# Typescript tests files must be inside sources and tests, othewise `INFO: Test execution data ignored for 80 unknown files, including:` is shown. +sonar.sources=src +sonar.tests=src +sonar.host.url=http://localhost:9001 + +sonar.test.inclusions=src/test/**/*.*, src/main/webapp/app/**/*.spec.ts +sonar.coverage.jacoco.xmlReportPaths=build/reports/jacoco/test/jacocoTestReport.xml +sonar.java.codeCoveragePlugin=jacoco +sonar.junit.reportPaths=build/test-results/test, build/test-results/integrationTest +sonar.testExecutionReportPaths=build/test-results/jest/TESTS-results-sonar.xml +sonar.javascript.lcov.reportPaths=build/test-results/lcov.info + +sonar.sourceEncoding=UTF-8 +sonar.exclusions=src/main/webapp/content/**/*.*, src/main/webapp/i18n/*.js, build/resources/main/static/**/*.* + +sonar.issue.ignore.multicriteria=S3437,S4502,S4684,S5145,UndocumentedApi +# Rule https://rules.sonarsource.com/java/RSPEC-3437 is ignored, as a JPA-managed field cannot be transient +sonar.issue.ignore.multicriteria.S3437.resourceKey=src/main/java/**/* +sonar.issue.ignore.multicriteria.S3437.ruleKey=squid:S3437 +# Rule https://rules.sonarsource.com/java/RSPEC-4502 is ignored, as for JWT tokens we are not subject to CSRF attack +sonar.issue.ignore.multicriteria.S4502.resourceKey=src/main/java/**/* +sonar.issue.ignore.multicriteria.S4502.ruleKey=java:S4502 +# Rule https://rules.sonarsource.com/java/RSPEC-4684 +sonar.issue.ignore.multicriteria.S4684.resourceKey=src/main/java/**/* +sonar.issue.ignore.multicriteria.S4684.ruleKey=java:S4684 +# Rule https://rules.sonarsource.com/java/RSPEC-5145 log filter is applied +sonar.issue.ignore.multicriteria.S5145.resourceKey=src/main/java/**/* +sonar.issue.ignore.multicriteria.S5145.ruleKey=javasecurity:S5145 +# Rule https://rules.sonarsource.com/java/RSPEC-1176 is ignored, as we want to follow "clean code" guidelines and classes, methods and arguments names should be self-explanatory +sonar.issue.ignore.multicriteria.UndocumentedApi.resourceKey=src/main/java/**/* +sonar.issue.ignore.multicriteria.UndocumentedApi.ruleKey=squid:UndocumentedApi diff --git a/src/main/docker/app.yml b/src/main/docker/app.yml new file mode 100644 index 00000000..8a0e8d29 --- /dev/null +++ b/src/main/docker/app.yml @@ -0,0 +1,41 @@ +# This configuration is intended for development purpose, it's **your** responsibility to harden it for production +version: '3.8' +services: + twentyonepoints-app: + image: twentyonepoints + environment: + - _JAVA_OPTIONS=-Xmx512m -Xms256m + - SPRING_PROFILES_ACTIVE=prod,api-docs + - MANAGEMENT_METRICS_EXPORT_PROMETHEUS_ENABLED=true + - SPRING_DATASOURCE_URL=jdbc:postgresql://twentyonepoints-postgresql:5432/TwentyOnePoints + - SPRING_LIQUIBASE_URL=jdbc:postgresql://twentyonepoints-postgresql:5432/TwentyOnePoints + - JHIPSTER_SLEEP=30 # gives time for other services to boot before the application + - SPRING_ELASTICSEARCH_URIS=http://twentyonepoints-elasticsearch:9200 + # If you want to expose these ports outside your dev PC, + # remove the "127.0.0.1:" prefix + ports: + - 127.0.0.1:8080:8080 + twentyonepoints-postgresql: + image: postgres:14.5 + # volumes: + # - ~/volumes/jhipster/TwentyOnePoints/postgresql/:/var/lib/postgresql/data/ + environment: + - POSTGRES_USER=TwentyOnePoints + - POSTGRES_PASSWORD= + - POSTGRES_HOST_AUTH_METHOD=trust + # If you want to expose these ports outside your dev PC, + # remove the "127.0.0.1:" prefix + ports: + - 127.0.0.1:5432:5432 + twentyonepoints-elasticsearch: + image: docker.elastic.co/elasticsearch/elasticsearch:7.17.4 + # volumes: + # - ~/volumes/jhipster/TwentyOnePoints/elasticsearch/:/usr/share/elasticsearch/data/ + # If you want to expose these ports outside your dev PC, + # remove the "127.0.0.1:" prefix + ports: + - 127.0.0.1:9200:9200 + - 127.0.0.1:9300:9300 + environment: + - 'ES_JAVA_OPTS=-Xms256m -Xmx256m' + - 'discovery.type=single-node' diff --git a/src/main/docker/central-server-config/README.md b/src/main/docker/central-server-config/README.md new file mode 100644 index 00000000..c3e9cfd5 --- /dev/null +++ b/src/main/docker/central-server-config/README.md @@ -0,0 +1 @@ +# Central configuration sources details diff --git a/src/main/docker/elasticsearch.yml b/src/main/docker/elasticsearch.yml new file mode 100644 index 00000000..e82df628 --- /dev/null +++ b/src/main/docker/elasticsearch.yml @@ -0,0 +1,26 @@ +# This configuration is intended for development purpose, it's **your** responsibility to harden it for production +version: '3.8' +services: + twentyonepoints-elasticsearch: + image: docker.elastic.co/elasticsearch/elasticsearch:7.17.4 + # volumes: + # - ~/volumes/jhipster/TwentyOnePoints/elasticsearch/:/usr/share/elasticsearch/data/ + # If you want to expose these ports outside your dev PC, + # remove the "127.0.0.1:" prefix + ports: + - 127.0.0.1:9200:9200 + - 127.0.0.1:9300:9300 + environment: + - 'xpack.security.enabled=false' + - 'discovery.type=single-node' + - 'bootstrap.memory_lock=true' + - 'ES_JAVA_OPTS=-Xms512m -Xmx512m' + ulimits: + memlock: + soft: -1 + hard: -1 + nofile: + soft: 65536 + hard: 65536 + cap_add: + - IPC_LOCK diff --git a/src/main/docker/grafana/provisioning/dashboards/JVM.json b/src/main/docker/grafana/provisioning/dashboards/JVM.json new file mode 100644 index 00000000..5104abcd --- /dev/null +++ b/src/main/docker/grafana/provisioning/dashboards/JVM.json @@ -0,0 +1,3778 @@ +{ + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": "-- Grafana --", + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "limit": 100, + "name": "Annotations & Alerts", + "showIn": 0, + "type": "dashboard" + }, + { + "datasource": "Prometheus", + "enable": true, + "expr": "resets(process_uptime_seconds{application=\"$application\", instance=\"$instance\"}[1m]) > 0", + "iconColor": "rgba(255, 96, 96, 1)", + "name": "Restart Detection", + "showIn": 0, + "step": "1m", + "tagKeys": "restart-tag", + "textFormat": "uptime reset", + "titleFormat": "Restart" + } + ] + }, + "description": "Dashboard for Micrometer instrumented applications (Java, Spring Boot, Micronaut)", + "editable": true, + "gnetId": 4701, + "graphTooltip": 1, + "iteration": 1553765841423, + "links": [], + "panels": [ + { + "content": "\n# Acknowledgments\n\nThank you to [Michael Weirauch](https://twitter.com/emwexx) for creating this dashboard: see original JVM (Micrometer) dashboard at [https://grafana.com/dashboards/4701](https://grafana.com/dashboards/4701)\n\n\n\n", + "gridPos": { + "h": 3, + "w": 24, + "x": 0, + "y": 0 + }, + "id": 141, + "links": [], + "mode": "markdown", + "timeFrom": null, + "timeShift": null, + "title": "Acknowledgments", + "type": "text" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 3 + }, + "id": 125, + "panels": [], + "repeat": null, + "title": "Quick Facts", + "type": "row" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": true, + "colors": ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"], + "datasource": "Prometheus", + "decimals": 1, + "editable": true, + "error": false, + "format": "s", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 3, + "w": 6, + "x": 0, + "y": 4 + }, + "height": "", + "id": 63, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "70%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": false + }, + "tableColumn": "", + "targets": [ + { + "expr": "process_uptime_seconds{application=\"$application\", instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "", + "metric": "", + "refId": "A", + "step": 14400 + } + ], + "thresholds": "", + "title": "Uptime", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": true, + "colors": ["rgba(245, 54, 54, 0.9)", "rgba(237, 129, 40, 0.89)", "rgba(50, 172, 45, 0.97)"], + "datasource": "Prometheus", + "decimals": null, + "editable": true, + "error": false, + "format": "dateTimeAsIso", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 3, + "w": 6, + "x": 6, + "y": 4 + }, + "height": "", + "id": 92, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "70%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": false + }, + "tableColumn": "", + "targets": [ + { + "expr": "process_start_time_seconds{application=\"$application\", instance=\"$instance\"}*1000", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "", + "metric": "", + "refId": "A", + "step": 14400 + } + ], + "thresholds": "", + "title": "Start time", + "type": "singlestat", + "valueFontSize": "70%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": true, + "colors": ["rgba(50, 172, 45, 0.97)", "rgba(237, 129, 40, 0.89)", "rgba(245, 54, 54, 0.9)"], + "datasource": "Prometheus", + "decimals": 2, + "editable": true, + "error": false, + "format": "percent", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 3, + "w": 6, + "x": 12, + "y": 4 + }, + "id": 65, + "interval": null, + "links": [], + "mappingType": 1, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "70%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": false + }, + "tableColumn": "", + "targets": [ + { + "expr": "sum(jvm_memory_used_bytes{application=\"$application\", instance=\"$instance\", area=\"heap\"})*100/sum(jvm_memory_max_bytes{application=\"$application\",instance=\"$instance\", area=\"heap\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "", + "refId": "A", + "step": 14400 + } + ], + "thresholds": "70,90", + "title": "Heap used", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + } + ], + "valueName": "current" + }, + { + "cacheTimeout": null, + "colorBackground": false, + "colorValue": true, + "colors": ["rgba(50, 172, 45, 0.97)", "rgba(237, 129, 40, 0.89)", "rgba(245, 54, 54, 0.9)"], + "datasource": "Prometheus", + "decimals": 2, + "editable": true, + "error": false, + "format": "percent", + "gauge": { + "maxValue": 100, + "minValue": 0, + "show": false, + "thresholdLabels": false, + "thresholdMarkers": true + }, + "gridPos": { + "h": 3, + "w": 6, + "x": 18, + "y": 4 + }, + "id": 75, + "interval": null, + "links": [], + "mappingType": 2, + "mappingTypes": [ + { + "name": "value to text", + "value": 1 + }, + { + "name": "range to text", + "value": 2 + } + ], + "maxDataPoints": 100, + "nullPointMode": "connected", + "nullText": null, + "postfix": "", + "postfixFontSize": "50%", + "prefix": "", + "prefixFontSize": "70%", + "rangeMaps": [ + { + "from": "null", + "text": "N/A", + "to": "null" + }, + { + "from": "-99999999999999999999999999999999", + "text": "N/A", + "to": "0" + } + ], + "sparkline": { + "fillColor": "rgba(31, 118, 189, 0.18)", + "full": false, + "lineColor": "rgb(31, 120, 193)", + "show": false + }, + "tableColumn": "", + "targets": [ + { + "expr": "sum(jvm_memory_used_bytes{application=\"$application\", instance=\"$instance\", area=\"nonheap\"})*100/sum(jvm_memory_max_bytes{application=\"$application\",instance=\"$instance\", area=\"nonheap\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "", + "refId": "A", + "step": 14400 + } + ], + "thresholds": "70,90", + "title": "Non-Heap used", + "type": "singlestat", + "valueFontSize": "80%", + "valueMaps": [ + { + "op": "=", + "text": "N/A", + "value": "null" + }, + { + "op": "=", + "text": "x", + "value": "" + } + ], + "valueName": "current" + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 7 + }, + "id": 126, + "panels": [], + "repeat": null, + "title": "I/O Overview", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 1, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 8 + }, + "id": 111, + "legend": { + "avg": false, + "current": true, + "max": false, + "min": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "paceLength": 10, + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(http_server_requests_seconds_count{application=\"$application\", instance=\"$instance\"}[1m]))", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "HTTP", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Rate", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "decimals": null, + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": { + "HTTP": "#890f02", + "HTTP - 5xx": "#bf1b00" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 1, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 8 + }, + "id": 112, + "legend": { + "avg": false, + "current": true, + "max": false, + "min": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "paceLength": 10, + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(http_server_requests_seconds_count{application=\"$application\", instance=\"$instance\", status=~\"5..\"}[1m]))", + "format": "time_series", + "intervalFactor": 1, + "legendFormat": "HTTP - 5xx", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Errors", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "decimals": null, + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 1, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 8 + }, + "id": 113, + "legend": { + "avg": false, + "current": true, + "max": false, + "min": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "paceLength": 10, + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(rate(http_server_requests_seconds_sum{application=\"$application\", instance=\"$instance\", status!~\"5..\"}[1m]))/sum(rate(http_server_requests_seconds_count{application=\"$application\", instance=\"$instance\", status!~\"5..\"}[1m]))", + "format": "time_series", + "hide": false, + "intervalFactor": 1, + "legendFormat": "HTTP - AVG", + "refId": "A" + }, + { + "expr": "max(http_server_requests_seconds_max{application=\"$application\", instance=\"$instance\", status!~\"5..\"})", + "format": "time_series", + "hide": false, + "intervalFactor": 1, + "legendFormat": "HTTP - MAX", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Duration", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 15 + }, + "id": 127, + "panels": [], + "repeat": null, + "title": "JVM Memory", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 16 + }, + "id": 24, + "legend": { + "avg": false, + "current": true, + "max": true, + "min": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "paceLength": 10, + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(jvm_memory_used_bytes{application=\"$application\", instance=\"$instance\", area=\"heap\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "used", + "metric": "", + "refId": "A", + "step": 2400 + }, + { + "expr": "sum(jvm_memory_committed_bytes{application=\"$application\", instance=\"$instance\", area=\"heap\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "committed", + "refId": "B", + "step": 2400 + }, + { + "expr": "sum(jvm_memory_max_bytes{application=\"$application\", instance=\"$instance\", area=\"heap\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "max", + "refId": "C", + "step": 2400 + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "JVM Heap", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "y-axis": true, + "y_formats": ["mbytes", "short"], + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 16 + }, + "id": 25, + "legend": { + "avg": false, + "current": true, + "max": true, + "min": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "paceLength": 10, + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(jvm_memory_used_bytes{application=\"$application\", instance=\"$instance\", area=\"nonheap\"})", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "used", + "metric": "", + "refId": "A", + "step": 2400 + }, + { + "expr": "sum(jvm_memory_committed_bytes{application=\"$application\", instance=\"$instance\", area=\"nonheap\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "committed", + "refId": "B", + "step": 2400 + }, + { + "expr": "sum(jvm_memory_max_bytes{application=\"$application\", instance=\"$instance\", area=\"nonheap\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "max", + "refId": "C", + "step": 2400 + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "JVM Non-Heap", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "y-axis": true, + "y_formats": ["mbytes", "short"], + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 16 + }, + "id": 26, + "legend": { + "alignAsTable": false, + "avg": false, + "current": true, + "max": true, + "min": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "paceLength": 10, + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "sum(jvm_memory_used_bytes{application=\"$application\", instance=\"$instance\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "used", + "metric": "", + "refId": "A", + "step": 2400 + }, + { + "expr": "sum(jvm_memory_committed_bytes{application=\"$application\", instance=\"$instance\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "committed", + "refId": "B", + "step": 2400 + }, + { + "expr": "sum(jvm_memory_max_bytes{application=\"$application\", instance=\"$instance\"})", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "max", + "refId": "C", + "step": 2400 + }, + { + "expr": "process_memory_vss_bytes{application=\"$application\", instance=\"$instance\"}", + "format": "time_series", + "hide": true, + "intervalFactor": 2, + "legendFormat": "vss", + "metric": "", + "refId": "D", + "step": 2400 + }, + { + "expr": "process_memory_rss_bytes{application=\"$application\", instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "rss", + "refId": "E", + "step": 2400 + }, + { + "expr": "process_memory_pss_bytes{application=\"$application\", instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "pss", + "refId": "F", + "step": 2400 + }, + { + "expr": "process_memory_swap_bytes{application=\"$application\", instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "swap", + "refId": "G", + "step": 2400 + }, + { + "expr": "process_memory_swappss_bytes{application=\"$application\", instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "swappss", + "refId": "H", + "step": 2400 + }, + { + "expr": "process_memory_pss_bytes{application=\"$application\", instance=\"$instance\"} + process_memory_swap_bytes{application=\"$application\", instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "phys (pss+swap)", + "refId": "I", + "step": 2400 + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "JVM Total", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "y-axis": true, + "y_formats": ["mbytes", "short"], + "yaxes": [ + { + "format": "bytes", + "label": "", + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 23 + }, + "id": 128, + "panels": [], + "repeat": null, + "title": "JVM Misc", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 0, + "y": 24 + }, + "id": 106, + "legend": { + "avg": false, + "current": true, + "max": true, + "min": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "paceLength": 10, + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "system_cpu_usage{application=\"$application\", instance=\"$instance\"}", + "format": "time_series", + "hide": false, + "intervalFactor": 1, + "legendFormat": "system", + "metric": "", + "refId": "A", + "step": 2400 + }, + { + "expr": "process_cpu_usage{application=\"$application\", instance=\"$instance\"}", + "format": "time_series", + "hide": false, + "intervalFactor": 1, + "legendFormat": "process", + "refId": "B" + }, + { + "expr": "avg_over_time(process_cpu_usage{application=\"$application\", instance=\"$instance\"}[1h])", + "format": "time_series", + "hide": false, + "intervalFactor": 1, + "legendFormat": "process-1h", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "CPU", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "y-axis": true, + "y_formats": ["short", "short"], + "yaxes": [ + { + "decimals": 1, + "format": "percentunit", + "label": "", + "logBase": 1, + "max": "1", + "min": 0, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 6, + "y": 24 + }, + "id": 93, + "legend": { + "avg": false, + "current": true, + "max": true, + "min": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "paceLength": 10, + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "system_load_average_1m{application=\"$application\", instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "system-1m", + "metric": "", + "refId": "A", + "step": 2400 + }, + { + "expr": "", + "format": "time_series", + "intervalFactor": 2, + "refId": "B" + }, + { + "expr": "system_cpu_count{application=\"$application\", instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "cpu", + "refId": "C" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Load", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "y-axis": true, + "y_formats": ["short", "short"], + "yaxes": [ + { + "decimals": 1, + "format": "short", + "label": "", + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 12, + "y": 24 + }, + "id": 32, + "legend": { + "avg": false, + "current": true, + "max": true, + "min": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "paceLength": 10, + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "jvm_threads_live{application=\"$application\", instance=\"$instance\"} or jvm_threads_live_threads{application=\"$application\", instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "live", + "metric": "", + "refId": "A", + "step": 2400 + }, + { + "expr": "jvm_threads_daemon{application=\"$application\", instance=\"$instance\"} or jvm_threads_daemon_threads{application=\"$application\", instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "daemon", + "metric": "", + "refId": "B", + "step": 2400 + }, + { + "expr": "jvm_threads_peak{application=\"$application\", instance=\"$instance\"} or jvm_threads_peak_threads{application=\"$application\", instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "peak", + "refId": "C", + "step": 2400 + }, + { + "expr": "process_threads{application=\"$application\", instance=\"$instance\"}", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "process", + "refId": "D", + "step": 2400 + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Threads", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "y-axis": true, + "y_formats": ["short", "short"], + "yaxes": [ + { + "decimals": 0, + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": { + "blocked": "#bf1b00", + "new": "#fce2de", + "runnable": "#7eb26d", + "terminated": "#511749", + "timed-waiting": "#c15c17", + "waiting": "#eab839" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 1, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 24 + }, + "id": 124, + "legend": { + "alignAsTable": false, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "paceLength": 10, + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "jvm_threads_states_threads{application=\"$application\", instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "{{state}}", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Thread States", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": { + "debug": "#1F78C1", + "error": "#BF1B00", + "info": "#508642", + "trace": "#6ED0E0", + "warn": "#EAB839" + }, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null + }, + "gridPos": { + "h": 7, + "w": 18, + "x": 0, + "y": 31 + }, + "height": "", + "id": 91, + "legend": { + "alignAsTable": false, + "avg": false, + "current": true, + "hideEmpty": false, + "hideZero": false, + "max": true, + "min": false, + "rightSide": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "paceLength": 10, + "percentage": true, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [ + { + "alias": "error", + "yaxis": 1 + }, + { + "alias": "warn", + "yaxis": 1 + } + ], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "increase(logback_events_total{application=\"$application\", instance=\"$instance\"}[1m])", + "format": "time_series", + "interval": "", + "intervalFactor": 2, + "legendFormat": "{{level}}", + "metric": "", + "refId": "A", + "step": 1200 + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Log Events (1m)", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "x-axis": true, + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "y-axis": true, + "y_formats": ["short", "short"], + "yaxes": [ + { + "decimals": 0, + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 31 + }, + "id": 61, + "legend": { + "avg": false, + "current": true, + "max": true, + "min": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "paceLength": 10, + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "process_open_fds{application=\"$application\", instance=\"$instance\"}", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "open", + "metric": "", + "refId": "A", + "step": 2400 + }, + { + "expr": "process_max_fds{application=\"$application\", instance=\"$instance\"}", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "max", + "metric": "", + "refId": "B", + "step": 2400 + }, + { + "expr": "process_files_open{application=\"$application\", instance=\"$instance\"} or process_files_open_files{application=\"$application\", instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "open", + "refId": "C" + }, + { + "expr": "process_files_max{application=\"$application\", instance=\"$instance\"} or process_files_max_files{application=\"$application\", instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "max", + "refId": "D" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "File Descriptors", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "y-axis": true, + "y_formats": ["short", "short"], + "yaxes": [ + { + "decimals": 0, + "format": "short", + "label": null, + "logBase": 10, + "max": null, + "min": 0, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 38 + }, + "id": 129, + "panels": [], + "repeat": "persistence_counts", + "title": "JVM Memory Pools (Heap)", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 39 + }, + "id": 3, + "legend": { + "alignAsTable": false, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "maxPerRow": 3, + "nullPointMode": "null", + "paceLength": 10, + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": "jvm_memory_pool_heap", + "scopedVars": { + "jvm_memory_pool_heap": { + "selected": false, + "text": "PS Eden Space", + "value": "PS Eden Space" + } + }, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "jvm_memory_used_bytes{application=\"$application\", instance=\"$instance\", id=\"$jvm_memory_pool_heap\"}", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "used", + "metric": "", + "refId": "A", + "step": 1800 + }, + { + "expr": "jvm_memory_committed_bytes{application=\"$application\", instance=\"$instance\", id=\"$jvm_memory_pool_heap\"}", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "commited", + "metric": "", + "refId": "B", + "step": 1800 + }, + { + "expr": "jvm_memory_max_bytes{application=\"$application\", instance=\"$instance\", id=\"$jvm_memory_pool_heap\"}", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "max", + "metric": "", + "refId": "C", + "step": 1800 + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "$jvm_memory_pool_heap", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "y-axis": true, + "y_formats": ["mbytes", "short"], + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 39 + }, + "id": 134, + "legend": { + "alignAsTable": false, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "maxPerRow": 3, + "nullPointMode": "null", + "paceLength": 10, + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "repeatIteration": 1553765841423, + "repeatPanelId": 3, + "scopedVars": { + "jvm_memory_pool_heap": { + "selected": false, + "text": "PS Old Gen", + "value": "PS Old Gen" + } + }, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "jvm_memory_used_bytes{application=\"$application\", instance=\"$instance\", id=\"$jvm_memory_pool_heap\"}", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "used", + "metric": "", + "refId": "A", + "step": 1800 + }, + { + "expr": "jvm_memory_committed_bytes{application=\"$application\", instance=\"$instance\", id=\"$jvm_memory_pool_heap\"}", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "commited", + "metric": "", + "refId": "B", + "step": 1800 + }, + { + "expr": "jvm_memory_max_bytes{application=\"$application\", instance=\"$instance\", id=\"$jvm_memory_pool_heap\"}", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "max", + "metric": "", + "refId": "C", + "step": 1800 + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "$jvm_memory_pool_heap", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "y-axis": true, + "y_formats": ["mbytes", "short"], + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 39 + }, + "id": 135, + "legend": { + "alignAsTable": false, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "maxPerRow": 3, + "nullPointMode": "null", + "paceLength": 10, + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "repeatIteration": 1553765841423, + "repeatPanelId": 3, + "scopedVars": { + "jvm_memory_pool_heap": { + "selected": false, + "text": "PS Survivor Space", + "value": "PS Survivor Space" + } + }, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "jvm_memory_used_bytes{application=\"$application\", instance=\"$instance\", id=\"$jvm_memory_pool_heap\"}", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "used", + "metric": "", + "refId": "A", + "step": 1800 + }, + { + "expr": "jvm_memory_committed_bytes{application=\"$application\", instance=\"$instance\", id=\"$jvm_memory_pool_heap\"}", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "commited", + "metric": "", + "refId": "B", + "step": 1800 + }, + { + "expr": "jvm_memory_max_bytes{application=\"$application\", instance=\"$instance\", id=\"$jvm_memory_pool_heap\"}", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "max", + "metric": "", + "refId": "C", + "step": 1800 + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "$jvm_memory_pool_heap", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "y-axis": true, + "y_formats": ["mbytes", "short"], + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 46 + }, + "id": 130, + "panels": [], + "repeat": null, + "title": "JVM Memory Pools (Non-Heap)", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 47 + }, + "id": 78, + "legend": { + "alignAsTable": false, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "maxPerRow": 3, + "nullPointMode": "null", + "paceLength": 10, + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": "jvm_memory_pool_nonheap", + "scopedVars": { + "jvm_memory_pool_nonheap": { + "selected": false, + "text": "Metaspace", + "value": "Metaspace" + } + }, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "jvm_memory_used_bytes{application=\"$application\", instance=\"$instance\", id=\"$jvm_memory_pool_nonheap\"}", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "used", + "metric": "", + "refId": "A", + "step": 1800 + }, + { + "expr": "jvm_memory_committed_bytes{application=\"$application\", instance=\"$instance\", id=\"$jvm_memory_pool_nonheap\"}", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "commited", + "metric": "", + "refId": "B", + "step": 1800 + }, + { + "expr": "jvm_memory_max_bytes{application=\"$application\", instance=\"$instance\", id=\"$jvm_memory_pool_nonheap\"}", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "max", + "metric": "", + "refId": "C", + "step": 1800 + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "$jvm_memory_pool_nonheap", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "y-axis": true, + "y_formats": ["mbytes", "short"], + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 47 + }, + "id": 136, + "legend": { + "alignAsTable": false, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "maxPerRow": 3, + "nullPointMode": "null", + "paceLength": 10, + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "repeatIteration": 1553765841423, + "repeatPanelId": 78, + "scopedVars": { + "jvm_memory_pool_nonheap": { + "selected": false, + "text": "Compressed Class Space", + "value": "Compressed Class Space" + } + }, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "jvm_memory_used_bytes{application=\"$application\", instance=\"$instance\", id=\"$jvm_memory_pool_nonheap\"}", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "used", + "metric": "", + "refId": "A", + "step": 1800 + }, + { + "expr": "jvm_memory_committed_bytes{application=\"$application\", instance=\"$instance\", id=\"$jvm_memory_pool_nonheap\"}", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "commited", + "metric": "", + "refId": "B", + "step": 1800 + }, + { + "expr": "jvm_memory_max_bytes{application=\"$application\", instance=\"$instance\", id=\"$jvm_memory_pool_nonheap\"}", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "max", + "metric": "", + "refId": "C", + "step": 1800 + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "$jvm_memory_pool_nonheap", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "y-axis": true, + "y_formats": ["mbytes", "short"], + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null + }, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 47 + }, + "id": 137, + "legend": { + "alignAsTable": false, + "avg": false, + "current": true, + "max": true, + "min": false, + "rightSide": false, + "show": true, + "total": false, + "values": true + }, + "lines": true, + "linewidth": 1, + "links": [], + "maxPerRow": 3, + "nullPointMode": "null", + "paceLength": 10, + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "repeat": null, + "repeatIteration": 1553765841423, + "repeatPanelId": 78, + "scopedVars": { + "jvm_memory_pool_nonheap": { + "selected": false, + "text": "Code Cache", + "value": "Code Cache" + } + }, + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "jvm_memory_used_bytes{application=\"$application\", instance=\"$instance\", id=\"$jvm_memory_pool_nonheap\"}", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "used", + "metric": "", + "refId": "A", + "step": 1800 + }, + { + "expr": "jvm_memory_committed_bytes{application=\"$application\", instance=\"$instance\", id=\"$jvm_memory_pool_nonheap\"}", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "commited", + "metric": "", + "refId": "B", + "step": 1800 + }, + { + "expr": "jvm_memory_max_bytes{application=\"$application\", instance=\"$instance\", id=\"$jvm_memory_pool_nonheap\"}", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "max", + "metric": "", + "refId": "C", + "step": 1800 + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "$jvm_memory_pool_nonheap", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "y-axis": true, + "y_formats": ["mbytes", "short"], + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 54 + }, + "id": 131, + "panels": [], + "repeat": null, + "title": "Garbage Collection", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 1, + "gridPos": { + "h": 7, + "w": 8, + "x": 0, + "y": 55 + }, + "id": 98, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "paceLength": 10, + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "rate(jvm_gc_pause_seconds_count{application=\"$application\", instance=\"$instance\"}[1m])", + "format": "time_series", + "hide": false, + "intervalFactor": 2, + "legendFormat": "{{action}} ({{cause}})", + "refId": "A" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Collections", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "ops", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": "", + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 1, + "gridPos": { + "h": 7, + "w": 8, + "x": 8, + "y": 55 + }, + "id": 101, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "paceLength": 10, + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "rate(jvm_gc_pause_seconds_sum{application=\"$application\", instance=\"$instance\"}[1m])/rate(jvm_gc_pause_seconds_count{application=\"$application\", instance=\"$instance\"}[1m])", + "format": "time_series", + "hide": false, + "instant": false, + "intervalFactor": 1, + "legendFormat": "avg {{action}} ({{cause}})", + "refId": "A" + }, + { + "expr": "jvm_gc_pause_seconds_max{application=\"$application\", instance=\"$instance\"}", + "format": "time_series", + "hide": false, + "instant": false, + "intervalFactor": 1, + "legendFormat": "max {{action}} ({{cause}})", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Pause Durations", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "s", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": "", + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "fill": 1, + "gridPos": { + "h": 7, + "w": 8, + "x": 16, + "y": 55 + }, + "id": 99, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "paceLength": 10, + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "rate(jvm_gc_memory_allocated_bytes_total{application=\"$application\", instance=\"$instance\"}[1m])", + "format": "time_series", + "interval": "", + "intervalFactor": 1, + "legendFormat": "allocated", + "refId": "A" + }, + { + "expr": "rate(jvm_gc_memory_promoted_bytes_total{application=\"$application\", instance=\"$instance\"}[1m])", + "format": "time_series", + "interval": "", + "intervalFactor": 1, + "legendFormat": "promoted", + "refId": "B" + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Allocated/Promoted", + "tooltip": { + "shared": true, + "sort": 0, + "value_type": "individual" + }, + "type": "graph", + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": "0", + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 62 + }, + "id": 132, + "panels": [], + "repeat": null, + "title": "Classloading", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 0, + "y": 63 + }, + "id": 37, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "paceLength": 10, + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "jvm_classes_loaded{application=\"$application\", instance=\"$instance\"} or jvm_classes_loaded_classes{application=\"$application\", instance=\"$instance\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "loaded", + "metric": "", + "refId": "A", + "step": 1200 + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Classes loaded", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "y-axis": true, + "y_formats": ["short", "short"], + "yaxes": [ + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null + }, + "gridPos": { + "h": 7, + "w": 12, + "x": 12, + "y": 63 + }, + "id": 38, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "paceLength": 10, + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "delta(jvm_classes_loaded{application=\"$application\",instance=\"$instance\"}[5m]) or delta(jvm_classes_loaded_classes{application=\"$application\",instance=\"$instance\"}[5m])", + "format": "time_series", + "hide": false, + "interval": "", + "intervalFactor": 2, + "legendFormat": "delta", + "metric": "", + "refId": "A", + "step": 1200 + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Class delta (5m)", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "y-axis": true, + "y_formats": ["ops", "short"], + "yaxes": [ + { + "decimals": null, + "format": "short", + "label": "", + "logBase": 1, + "max": null, + "min": null, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "collapsed": false, + "gridPos": { + "h": 1, + "w": 24, + "x": 0, + "y": 70 + }, + "id": 133, + "panels": [], + "repeat": null, + "title": "Buffer Pools", + "type": "row" + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 0, + "y": 71 + }, + "id": 33, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "paceLength": 10, + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "jvm_buffer_memory_used_bytes{application=\"$application\", instance=\"$instance\", id=\"direct\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "used", + "metric": "", + "refId": "A", + "step": 2400 + }, + { + "expr": "jvm_buffer_total_capacity_bytes{application=\"$application\", instance=\"$instance\", id=\"direct\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "capacity", + "metric": "", + "refId": "B", + "step": 2400 + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Direct Buffers", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "y-axis": true, + "y_formats": ["short", "short"], + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 6, + "y": 71 + }, + "id": 83, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "paceLength": 10, + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "jvm_buffer_count{application=\"$application\", instance=\"$instance\", id=\"direct\"} or jvm_buffer_count_buffers{application=\"$application\", instance=\"$instance\", id=\"direct\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "count", + "metric": "", + "refId": "A", + "step": 2400 + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Direct Buffers", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "y-axis": true, + "y_formats": ["short", "short"], + "yaxes": [ + { + "decimals": 0, + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 12, + "y": 71 + }, + "id": 85, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "paceLength": 10, + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "jvm_buffer_memory_used_bytes{application=\"$application\", instance=\"$instance\", id=\"mapped\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "used", + "metric": "", + "refId": "A", + "step": 2400 + }, + { + "expr": "jvm_buffer_total_capacity_bytes{application=\"$application\", instance=\"$instance\", id=\"mapped\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "capacity", + "metric": "", + "refId": "B", + "step": 2400 + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Mapped Buffers", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "y-axis": true, + "y_formats": ["short", "short"], + "yaxes": [ + { + "format": "bytes", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + }, + { + "aliasColors": {}, + "bars": false, + "dashLength": 10, + "dashes": false, + "datasource": "Prometheus", + "editable": true, + "error": false, + "fill": 1, + "grid": { + "leftLogBase": 1, + "leftMax": null, + "leftMin": null, + "rightLogBase": 1, + "rightMax": null, + "rightMin": null + }, + "gridPos": { + "h": 7, + "w": 6, + "x": 18, + "y": 71 + }, + "id": 84, + "legend": { + "avg": false, + "current": false, + "max": false, + "min": false, + "show": true, + "total": false, + "values": false + }, + "lines": true, + "linewidth": 1, + "links": [], + "nullPointMode": "null", + "paceLength": 10, + "percentage": false, + "pointradius": 5, + "points": false, + "renderer": "flot", + "seriesOverrides": [], + "spaceLength": 10, + "stack": false, + "steppedLine": false, + "targets": [ + { + "expr": "jvm_buffer_count{application=\"$application\", instance=\"$instance\", id=\"mapped\"} or jvm_buffer_count_buffers{application=\"$application\", instance=\"$instance\", id=\"mapped\"}", + "format": "time_series", + "intervalFactor": 2, + "legendFormat": "count", + "metric": "", + "refId": "A", + "step": 2400 + } + ], + "thresholds": [], + "timeFrom": null, + "timeRegions": [], + "timeShift": null, + "title": "Mapped Buffers", + "tooltip": { + "msResolution": false, + "shared": true, + "sort": 0, + "value_type": "cumulative" + }, + "type": "graph", + "x-axis": true, + "xaxis": { + "buckets": null, + "mode": "time", + "name": null, + "show": true, + "values": [] + }, + "y-axis": true, + "y_formats": ["short", "short"], + "yaxes": [ + { + "decimals": 0, + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": 0, + "show": true + }, + { + "format": "short", + "label": null, + "logBase": 1, + "max": null, + "min": null, + "show": true + } + ], + "yaxis": { + "align": false, + "alignLevel": null + } + } + ], + "refresh": "10s", + "schemaVersion": 18, + "style": "dark", + "tags": [], + "templating": { + "list": [ + { + "allValue": null, + "current": { + "text": "test", + "value": "test" + }, + "datasource": "Prometheus", + "definition": "", + "hide": 0, + "includeAll": false, + "label": "Application", + "multi": false, + "name": "application", + "options": [], + "query": "label_values(application)", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allFormat": "glob", + "allValue": null, + "current": { + "text": "localhost:8080", + "value": "localhost:8080" + }, + "datasource": "Prometheus", + "definition": "", + "hide": 0, + "includeAll": false, + "label": "Instance", + "multi": false, + "multiFormat": "glob", + "name": "instance", + "options": [], + "query": "label_values(jvm_memory_used_bytes{application=\"$application\"}, instance)", + "refresh": 2, + "regex": "", + "skipUrlSync": false, + "sort": 0, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allFormat": "glob", + "allValue": null, + "current": { + "text": "All", + "value": "$__all" + }, + "datasource": "Prometheus", + "definition": "", + "hide": 0, + "includeAll": true, + "label": "JVM Memory Pools Heap", + "multi": false, + "multiFormat": "glob", + "name": "jvm_memory_pool_heap", + "options": [], + "query": "label_values(jvm_memory_used_bytes{application=\"$application\", instance=\"$instance\", area=\"heap\"},id)", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 1, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + }, + { + "allFormat": "glob", + "allValue": null, + "current": { + "text": "All", + "value": "$__all" + }, + "datasource": "Prometheus", + "definition": "", + "hide": 0, + "includeAll": true, + "label": "JVM Memory Pools Non-Heap", + "multi": false, + "multiFormat": "glob", + "name": "jvm_memory_pool_nonheap", + "options": [], + "query": "label_values(jvm_memory_used_bytes{application=\"$application\", instance=\"$instance\", area=\"nonheap\"},id)", + "refresh": 1, + "regex": "", + "skipUrlSync": false, + "sort": 2, + "tagValuesQuery": "", + "tags": [], + "tagsQuery": "", + "type": "query", + "useTags": false + } + ] + }, + "time": { + "from": "now-30m", + "to": "now" + }, + "timepicker": { + "now": true, + "refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"], + "time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"] + }, + "timezone": "browser", + "title": "JVM (Micrometer)", + "uid": "Ud1CFe3iz", + "version": 1 +} diff --git a/src/main/docker/grafana/provisioning/dashboards/dashboard.yml b/src/main/docker/grafana/provisioning/dashboards/dashboard.yml new file mode 100644 index 00000000..4817a83a --- /dev/null +++ b/src/main/docker/grafana/provisioning/dashboards/dashboard.yml @@ -0,0 +1,11 @@ +apiVersion: 1 + +providers: + - name: 'Prometheus' + orgId: 1 + folder: '' + type: file + disableDeletion: false + editable: true + options: + path: /etc/grafana/provisioning/dashboards diff --git a/src/main/docker/grafana/provisioning/datasources/datasource.yml b/src/main/docker/grafana/provisioning/datasources/datasource.yml new file mode 100644 index 00000000..57b2bb3e --- /dev/null +++ b/src/main/docker/grafana/provisioning/datasources/datasource.yml @@ -0,0 +1,50 @@ +apiVersion: 1 + +# list of datasources that should be deleted from the database +deleteDatasources: + - name: Prometheus + orgId: 1 + +# list of datasources to insert/update depending +# whats available in the database +datasources: + # name of the datasource. Required + - name: Prometheus + # datasource type. Required + type: prometheus + # access mode. direct or proxy. Required + access: proxy + # org id. will default to orgId 1 if not specified + orgId: 1 + # url + # On MacOS, replace localhost by host.docker.internal + url: http://localhost:9090 + # database password, if used + password: + # database user, if used + user: + # database name, if used + database: + # enable/disable basic auth + basicAuth: false + # basic auth username + basicAuthUser: admin + # basic auth password + basicAuthPassword: admin + # enable/disable with credentials headers + withCredentials: + # mark as default datasource. Max one per org + isDefault: true + # fields that will be converted to json and stored in json_data + jsonData: + graphiteVersion: '1.1' + tlsAuth: false + tlsAuthWithCACert: false + # json object of data that will be encrypted. + secureJsonData: + tlsCACert: '...' + tlsClientCert: '...' + tlsClientKey: '...' + version: 1 + # allow users to edit datasources from the UI. + editable: true diff --git a/src/main/docker/jhipster-control-center.yml b/src/main/docker/jhipster-control-center.yml new file mode 100644 index 00000000..22fa07c1 --- /dev/null +++ b/src/main/docker/jhipster-control-center.yml @@ -0,0 +1,52 @@ +## How to use JHCC docker compose +# To allow JHCC to reach JHipster application from a docker container note that we set the host as host.docker.internal +# To reach the application from a browser, you need to add '127.0.0.1 host.docker.internal' to your hosts file. +### Discovery mode +# JHCC support 3 kinds of discovery mode: Consul, Eureka and static +# In order to use one, please set SPRING_PROFILES_ACTIVE to one (and only one) of this values: consul,eureka,static +### Discovery properties +# According to the discovery mode choose as Spring profile, you have to set the right properties +# please note that current properties are set to run JHCC with default values, personalize them if needed +# and remove those from other modes. You can only have one mode active. +#### Eureka +# - EUREKA_CLIENT_SERVICE_URL_DEFAULTZONE=http://admin:admin@host.docker.internal:8761/eureka/ +#### Consul +# - SPRING_CLOUD_CONSUL_HOST=host.docker.internal +# - SPRING_CLOUD_CONSUL_PORT=8500 +#### Static +# Add instances to "MyApp" +# - SPRING_CLOUD_DISCOVERY_CLIENT_SIMPLE_INSTANCES_MYAPP_0_URI=http://host.docker.internal:8081 +# - SPRING_CLOUD_DISCOVERY_CLIENT_SIMPLE_INSTANCES_MYAPP_1_URI=http://host.docker.internal:8082 +# Or add a new application named MyNewApp +# - SPRING_CLOUD_DISCOVERY_CLIENT_SIMPLE_INSTANCES_MYNEWAPP_0_URI=http://host.docker.internal:8080 +# This configuration is intended for development purpose, it's **your** responsibility to harden it for production + +#### IMPORTANT +# If you choose Consul or Eureka mode: +# Do not forget to remove the prefix "127.0.0.1" in front of their port in order to expose them. +# This is required because JHCC need to communicate with Consul or Eureka. +# - In Consul mode, the ports are in the consul.yml file. +# - In Eureka mode, the ports are in the jhipster-registry.yml file. + +version: '3.8' +services: + jhipster-control-center: + image: 'jhipster/jhipster-control-center:v0.5.0' + command: + - /bin/sh + - -c + # Patch /etc/hosts to support resolving host.docker.internal to the internal IP address used by the host in all OSes + - echo "`ip route | grep default | cut -d ' ' -f3` host.docker.internal" | tee -a /etc/hosts > /dev/null && java -jar /jhipster-control-center.jar + environment: + - _JAVA_OPTIONS=-Xmx512m -Xms256m + - SPRING_PROFILES_ACTIVE=prod,api-docs,static + - JHIPSTER_SLEEP=30 # gives time for other services to boot before the application + - SPRING_SECURITY_USER_PASSWORD=admin + # The token should have the same value than the one declared in you Spring configuration under the jhipster.security.authentication.jwt.base64-secret configuration's entry + - JHIPSTER_SECURITY_AUTHENTICATION_JWT_BASE64_SECRET=YjAwZDU2ODI3NzNjZjk0NjVhY2UzNGViZmY0YjY1ZGY2MDI5MTc5Y2FlZWYxNzE5Yjg5ZjMxOGZhZTgwZjA5NTFkN2VmNDM3ZGMyZDNjZTViNDAwYTg4NmNlMmM2ZDkxZTMwMDk5YmUxNWNkZmE0YTNiNDlhMGNhZmM4OTk1NmQ= + - SPRING_CLOUD_DISCOVERY_CLIENT_SIMPLE_INSTANCES_TWENTYONEPOINTS_0_URI=http://host.docker.internal:8080 + - LOGGING_FILE_NAME=/tmp/jhipster-control-center.log + # If you want to expose these ports outside your dev PC, + # remove the "127.0.0.1:" prefix + ports: + - 127.0.0.1:7419:7419 diff --git a/src/main/docker/jib/entrypoint.sh b/src/main/docker/jib/entrypoint.sh new file mode 100644 index 00000000..5df6f158 --- /dev/null +++ b/src/main/docker/jib/entrypoint.sh @@ -0,0 +1,39 @@ +#!/bin/bash + +echo "The application will start in ${JHIPSTER_SLEEP}s..." && sleep ${JHIPSTER_SLEEP} + +# usage: file_env VAR [DEFAULT] +# ie: file_env 'XYZ_DB_PASSWORD' 'example' +# (will allow for "$XYZ_DB_PASSWORD_FILE" to fill in the value of +# "$XYZ_DB_PASSWORD" from a file, especially for Docker's secrets feature) +file_env() { + local var="$1" + local fileVar="${var}_FILE" + local def="${2:-}" + if [[ ${!var:-} && ${!fileVar:-} ]]; then + echo >&2 "error: both $var and $fileVar are set (but are exclusive)" + exit 1 + fi + local val="$def" + if [[ ${!var:-} ]]; then + val="${!var}" + elif [[ ${!fileVar:-} ]]; then + val="$(< "${!fileVar}")" + fi + + if [[ -n $val ]]; then + export "$var"="$val" + fi + + unset "$fileVar" +} + +file_env 'SPRING_DATASOURCE_URL' +file_env 'SPRING_DATASOURCE_USERNAME' +file_env 'SPRING_DATASOURCE_PASSWORD' +file_env 'SPRING_LIQUIBASE_URL' +file_env 'SPRING_LIQUIBASE_USER' +file_env 'SPRING_LIQUIBASE_PASSWORD' +file_env 'JHIPSTER_REGISTRY_PASSWORD' + +exec java ${JAVA_OPTS} -noverify -XX:+AlwaysPreTouch -Djava.security.egd=file:/dev/./urandom -cp /app/resources/:/app/classes/:/app/libs/* "org.jhipster.health.TwentyOnePointsApp" "$@" diff --git a/src/main/docker/monitoring.yml b/src/main/docker/monitoring.yml new file mode 100644 index 00000000..1786b152 --- /dev/null +++ b/src/main/docker/monitoring.yml @@ -0,0 +1,31 @@ +# This configuration is intended for development purpose, it's **your** responsibility to harden it for production +version: '3.8' +services: + twentyonepoints-prometheus: + image: prom/prometheus:v2.38.0 + volumes: + - ./prometheus/:/etc/prometheus/ + command: + - '--config.file=/etc/prometheus/prometheus.yml' + # If you want to expose these ports outside your dev PC, + # remove the "127.0.0.1:" prefix + ports: + - 127.0.0.1:9090:9090 + # On MacOS, remove next line and replace localhost by host.docker.internal in prometheus/prometheus.yml and + # grafana/provisioning/datasources/datasource.yml + network_mode: 'host' # to test locally running service + twentyonepoints-grafana: + image: grafana/grafana:9.1.0 + volumes: + - ./grafana/provisioning/:/etc/grafana/provisioning/ + environment: + - GF_SECURITY_ADMIN_PASSWORD=admin + - GF_USERS_ALLOW_SIGN_UP=false + - GF_INSTALL_PLUGINS=grafana-piechart-panel + # If you want to expose these ports outside your dev PC, + # remove the "127.0.0.1:" prefix + ports: + - 127.0.0.1:3000:3000 + # On MacOS, remove next line and replace localhost by host.docker.internal in prometheus/prometheus.yml and + # grafana/provisioning/datasources/datasource.yml + network_mode: 'host' # to test locally running service diff --git a/src/main/docker/postgresql.yml b/src/main/docker/postgresql.yml new file mode 100644 index 00000000..4d2d7bc0 --- /dev/null +++ b/src/main/docker/postgresql.yml @@ -0,0 +1,15 @@ +# This configuration is intended for development purpose, it's **your** responsibility to harden it for production +version: '3.8' +services: + twentyonepoints-postgresql: + image: postgres:14.5 + # volumes: + # - ~/volumes/jhipster/TwentyOnePoints/postgresql/:/var/lib/postgresql/data/ + environment: + - POSTGRES_USER=TwentyOnePoints + - POSTGRES_PASSWORD= + - POSTGRES_HOST_AUTH_METHOD=trust + # If you want to expose these ports outside your dev PC, + # remove the "127.0.0.1:" prefix + ports: + - 127.0.0.1:5432:5432 diff --git a/src/main/docker/prometheus/prometheus.yml b/src/main/docker/prometheus/prometheus.yml new file mode 100644 index 00000000..b370a2f4 --- /dev/null +++ b/src/main/docker/prometheus/prometheus.yml @@ -0,0 +1,31 @@ +# Sample global config for monitoring JHipster applications +global: + scrape_interval: 15s # By default, scrape targets every 15 seconds. + evaluation_interval: 15s # By default, scrape targets every 15 seconds. + # scrape_timeout is set to the global default (10s). + + # Attach these labels to any time series or alerts when communicating with + # external systems (federation, remote storage, Alertmanager). + external_labels: + monitor: 'jhipster' + +# A scrape configuration containing exactly one endpoint to scrape: +# Here it's Prometheus itself. +scrape_configs: + # The job name is added as a label `job=` to any timeseries scraped from this config. + - job_name: 'prometheus' + + # Override the global default and scrape targets from this job every 5 seconds. + scrape_interval: 5s + + # scheme defaults to 'http' enable https in case your application is server via https + #scheme: https + # basic auth is not needed by default. See https://www.jhipster.tech/monitoring/#configuring-metrics-forwarding for details + #basic_auth: + # username: admin + # password: admin + metrics_path: /management/prometheus + static_configs: + - targets: + # On MacOS, replace localhost by host.docker.internal + - localhost:8080 diff --git a/src/main/docker/sonar.yml b/src/main/docker/sonar.yml new file mode 100644 index 00000000..58a6be3f --- /dev/null +++ b/src/main/docker/sonar.yml @@ -0,0 +1,13 @@ +# This configuration is intended for development purpose, it's **your** responsibility to harden it for production +version: '3.8' +services: + twentyonepoints-sonar: + image: sonarqube:9.6.0-community + # Authentication is turned off for out of the box experience while trying out SonarQube + # For real use cases delete sonar.forceAuthentication variable or set sonar.forceAuthentication=true + environment: + - sonar.forceAuthentication=false + # If you want to expose these ports outside your dev PC, + # remove the "127.0.0.1:" prefix + ports: + - 127.0.0.1:9001:9000 diff --git a/src/main/docker/zipkin.yml b/src/main/docker/zipkin.yml new file mode 100644 index 00000000..07938b9e --- /dev/null +++ b/src/main/docker/zipkin.yml @@ -0,0 +1,7 @@ +# This configuration is intended for development purpose, it's **your** responsibility to harden it for production +version: '3.8' +services: + zipkin: + image: openzipkin/zipkin:2.23 + ports: + - 127.0.0.1:9411:9411 diff --git a/src/main/java/org/jhipster/health/ApplicationWebXml.java b/src/main/java/org/jhipster/health/ApplicationWebXml.java new file mode 100644 index 00000000..9b7aaefe --- /dev/null +++ b/src/main/java/org/jhipster/health/ApplicationWebXml.java @@ -0,0 +1,19 @@ +package org.jhipster.health; + +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; +import tech.jhipster.config.DefaultProfileUtil; + +/** + * This is a helper Java class that provides an alternative to creating a {@code web.xml}. + * This will be invoked only when the application is deployed to a Servlet container like Tomcat, JBoss etc. + */ +public class ApplicationWebXml extends SpringBootServletInitializer { + + @Override + protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { + // set a default to use when no profile is configured. + DefaultProfileUtil.addDefaultProfile(application.application()); + return application.sources(TwentyOnePointsApp.class); + } +} diff --git a/src/main/java/org/jhipster/health/GeneratedByJHipster.java b/src/main/java/org/jhipster/health/GeneratedByJHipster.java new file mode 100644 index 00000000..18fa99f5 --- /dev/null +++ b/src/main/java/org/jhipster/health/GeneratedByJHipster.java @@ -0,0 +1,13 @@ +package org.jhipster.health; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import javax.annotation.Generated; + +@Generated(value = "JHipster", comments = "Generated by JHipster 7.9.3") +@Retention(RetentionPolicy.SOURCE) +@Target({ ElementType.TYPE }) +public @interface GeneratedByJHipster { +} diff --git a/src/main/java/org/jhipster/health/TwentyOnePointsApp.java b/src/main/java/org/jhipster/health/TwentyOnePointsApp.java new file mode 100644 index 00000000..c83a4b82 --- /dev/null +++ b/src/main/java/org/jhipster/health/TwentyOnePointsApp.java @@ -0,0 +1,105 @@ +package org.jhipster.health; + +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Arrays; +import java.util.Collection; +import java.util.Optional; +import javax.annotation.PostConstruct; +import org.apache.commons.lang3.StringUtils; +import org.jhipster.health.config.ApplicationProperties; +import org.jhipster.health.config.CRLFLogConverter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.core.env.Environment; +import tech.jhipster.config.DefaultProfileUtil; +import tech.jhipster.config.JHipsterConstants; + +@SpringBootApplication +@EnableConfigurationProperties({ LiquibaseProperties.class, ApplicationProperties.class }) +public class TwentyOnePointsApp { + + private static final Logger log = LoggerFactory.getLogger(TwentyOnePointsApp.class); + + private final Environment env; + + public TwentyOnePointsApp(Environment env) { + this.env = env; + } + + /** + * Initializes TwentyOnePoints. + *

+ * Spring profiles can be configured with a program argument --spring.profiles.active=your-active-profile + *

+ * You can find more information on how profiles work with JHipster on https://www.jhipster.tech/profiles/. + */ + @PostConstruct + public void initApplication() { + Collection activeProfiles = Arrays.asList(env.getActiveProfiles()); + if ( + activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT) && + activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_PRODUCTION) + ) { + log.error( + "You have misconfigured your application! It should not run " + "with both the 'dev' and 'prod' profiles at the same time." + ); + } + if ( + activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT) && + activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_CLOUD) + ) { + log.error( + "You have misconfigured your application! It should not " + "run with both the 'dev' and 'cloud' profiles at the same time." + ); + } + } + + /** + * Main method, used to run the application. + * + * @param args the command line arguments. + */ + public static void main(String[] args) { + SpringApplication app = new SpringApplication(TwentyOnePointsApp.class); + DefaultProfileUtil.addDefaultProfile(app); + Environment env = app.run(args).getEnvironment(); + logApplicationStartup(env); + } + + private static void logApplicationStartup(Environment env) { + String protocol = Optional.ofNullable(env.getProperty("server.ssl.key-store")).map(key -> "https").orElse("http"); + String serverPort = env.getProperty("server.port"); + String contextPath = Optional + .ofNullable(env.getProperty("server.servlet.context-path")) + .filter(StringUtils::isNotBlank) + .orElse("/"); + String hostAddress = "localhost"; + try { + hostAddress = InetAddress.getLocalHost().getHostAddress(); + } catch (UnknownHostException e) { + log.warn("The host name could not be determined, using `localhost` as fallback"); + } + log.info( + CRLFLogConverter.CRLF_SAFE_MARKER, + "\n----------------------------------------------------------\n\t" + + "Application '{}' is running! Access URLs:\n\t" + + "Local: \t\t{}://localhost:{}{}\n\t" + + "External: \t{}://{}:{}{}\n\t" + + "Profile(s): \t{}\n----------------------------------------------------------", + env.getProperty("spring.application.name"), + protocol, + serverPort, + contextPath, + protocol, + hostAddress, + serverPort, + contextPath, + env.getActiveProfiles().length == 0 ? env.getDefaultProfiles() : env.getActiveProfiles() + ); + } +} diff --git a/src/main/java/org/jhipster/health/aop/logging/LoggingAspect.java b/src/main/java/org/jhipster/health/aop/logging/LoggingAspect.java new file mode 100644 index 00000000..40effe60 --- /dev/null +++ b/src/main/java/org/jhipster/health/aop/logging/LoggingAspect.java @@ -0,0 +1,115 @@ +package org.jhipster.health.aop.logging; + +import java.util.Arrays; +import org.aspectj.lang.JoinPoint; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.AfterThrowing; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.env.Environment; +import org.springframework.core.env.Profiles; +import tech.jhipster.config.JHipsterConstants; + +/** + * Aspect for logging execution of service and repository Spring components. + * + * By default, it only runs with the "dev" profile. + */ +@Aspect +public class LoggingAspect { + + private final Environment env; + + public LoggingAspect(Environment env) { + this.env = env; + } + + /** + * Pointcut that matches all repositories, services and Web REST endpoints. + */ + @Pointcut( + "within(@org.springframework.stereotype.Repository *)" + + " || within(@org.springframework.stereotype.Service *)" + + " || within(@org.springframework.web.bind.annotation.RestController *)" + ) + public void springBeanPointcut() { + // Method is empty as this is just a Pointcut, the implementations are in the advices. + } + + /** + * Pointcut that matches all Spring beans in the application's main packages. + */ + @Pointcut( + "within(org.jhipster.health.repository..*)" + + " || within(org.jhipster.health.service..*)" + + " || within(org.jhipster.health.web.rest..*)" + ) + public void applicationPackagePointcut() { + // Method is empty as this is just a Pointcut, the implementations are in the advices. + } + + /** + * Retrieves the {@link Logger} associated to the given {@link JoinPoint}. + * + * @param joinPoint join point we want the logger for. + * @return {@link Logger} associated to the given {@link JoinPoint}. + */ + private Logger logger(JoinPoint joinPoint) { + return LoggerFactory.getLogger(joinPoint.getSignature().getDeclaringTypeName()); + } + + /** + * Advice that logs methods throwing exceptions. + * + * @param joinPoint join point for advice. + * @param e exception. + */ + @AfterThrowing(pointcut = "applicationPackagePointcut() && springBeanPointcut()", throwing = "e") + public void logAfterThrowing(JoinPoint joinPoint, Throwable e) { + if (env.acceptsProfiles(Profiles.of(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT))) { + logger(joinPoint) + .error( + "Exception in {}() with cause = '{}' and exception = '{}'", + joinPoint.getSignature().getName(), + e.getCause() != null ? e.getCause() : "NULL", + e.getMessage(), + e + ); + } else { + logger(joinPoint) + .error( + "Exception in {}() with cause = {}", + joinPoint.getSignature().getName(), + e.getCause() != null ? e.getCause() : "NULL" + ); + } + } + + /** + * Advice that logs when a method is entered and exited. + * + * @param joinPoint join point for advice. + * @return result. + * @throws Throwable throws {@link IllegalArgumentException}. + */ + @Around("applicationPackagePointcut() && springBeanPointcut()") + public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable { + Logger log = logger(joinPoint); + if (log.isDebugEnabled()) { + log.debug("Enter: {}() with argument[s] = {}", joinPoint.getSignature().getName(), Arrays.toString(joinPoint.getArgs())); + } + try { + Object result = joinPoint.proceed(); + if (log.isDebugEnabled()) { + log.debug("Exit: {}() with result = {}", joinPoint.getSignature().getName(), result); + } + return result; + } catch (IllegalArgumentException e) { + log.error("Illegal argument: {} in {}()", Arrays.toString(joinPoint.getArgs()), joinPoint.getSignature().getName()); + throw e; + } + } +} diff --git a/src/main/java/org/jhipster/health/config/ApplicationProperties.java b/src/main/java/org/jhipster/health/config/ApplicationProperties.java new file mode 100644 index 00000000..91ae4733 --- /dev/null +++ b/src/main/java/org/jhipster/health/config/ApplicationProperties.java @@ -0,0 +1,32 @@ +package org.jhipster.health.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Properties specific to Android Software Portal. + *

+ * Properties are configured in the {@code application.yml} file. + * See {@link tech.jhipster.config.JHipsterProperties} for a good example. + */ +@ConfigurationProperties(prefix = "application", ignoreUnknownFields = false) +public class ApplicationProperties { + + private final Elasticsearch elasticsearch = new Elasticsearch(); + + public Elasticsearch getElasticsearch() { + return elasticsearch; + } + + public static class Elasticsearch { + + private boolean reindexOnStartup; + + public boolean getReindexOnStartup() { + return reindexOnStartup; + } + + public void setReindexOnStartup(boolean reindexOnStartup) { + this.reindexOnStartup = reindexOnStartup; + } + } +} diff --git a/src/main/java/org/jhipster/health/config/AsyncConfiguration.java b/src/main/java/org/jhipster/health/config/AsyncConfiguration.java new file mode 100644 index 00000000..ecdc65aa --- /dev/null +++ b/src/main/java/org/jhipster/health/config/AsyncConfiguration.java @@ -0,0 +1,48 @@ +package org.jhipster.health.config; + +import java.util.concurrent.Executor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; +import org.springframework.aop.interceptor.SimpleAsyncUncaughtExceptionHandler; +import org.springframework.boot.autoconfigure.task.TaskExecutionProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import org.springframework.scheduling.annotation.AsyncConfigurer; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.annotation.EnableScheduling; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import tech.jhipster.async.ExceptionHandlingAsyncTaskExecutor; + +@Configuration +@EnableAsync +@EnableScheduling +@Profile("!testdev & !testprod") +public class AsyncConfiguration implements AsyncConfigurer { + + private final Logger log = LoggerFactory.getLogger(AsyncConfiguration.class); + + private final TaskExecutionProperties taskExecutionProperties; + + public AsyncConfiguration(TaskExecutionProperties taskExecutionProperties) { + this.taskExecutionProperties = taskExecutionProperties; + } + + @Override + @Bean(name = "taskExecutor") + public Executor getAsyncExecutor() { + log.debug("Creating Async Task Executor"); + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(taskExecutionProperties.getPool().getCoreSize()); + executor.setMaxPoolSize(taskExecutionProperties.getPool().getMaxSize()); + executor.setQueueCapacity(taskExecutionProperties.getPool().getQueueCapacity()); + executor.setThreadNamePrefix(taskExecutionProperties.getThreadNamePrefix()); + return new ExceptionHandlingAsyncTaskExecutor(executor); + } + + @Override + public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { + return new SimpleAsyncUncaughtExceptionHandler(); + } +} diff --git a/src/main/java/org/jhipster/health/config/CRLFLogConverter.java b/src/main/java/org/jhipster/health/config/CRLFLogConverter.java new file mode 100644 index 00000000..d445260d --- /dev/null +++ b/src/main/java/org/jhipster/health/config/CRLFLogConverter.java @@ -0,0 +1,56 @@ +package org.jhipster.health.config; + +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.pattern.CompositeConverter; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import org.slf4j.Marker; +import org.slf4j.MarkerFactory; +import org.springframework.boot.ansi.AnsiColor; +import org.springframework.boot.ansi.AnsiElement; +import org.springframework.boot.ansi.AnsiOutput; +import org.springframework.boot.ansi.AnsiStyle; + +public class CRLFLogConverter extends CompositeConverter { + + public static final Marker CRLF_SAFE_MARKER = MarkerFactory.getMarker("CRLF_SAFE"); + + private static final String[] SAFE_LOGGERS = { "org.hibernate" }; + private static final Map ELEMENTS; + + static { + Map ansiElements = new HashMap<>(); + ansiElements.put("faint", AnsiStyle.FAINT); + ansiElements.put("red", AnsiColor.RED); + ansiElements.put("green", AnsiColor.GREEN); + ansiElements.put("yellow", AnsiColor.YELLOW); + ansiElements.put("blue", AnsiColor.BLUE); + ansiElements.put("magenta", AnsiColor.MAGENTA); + ansiElements.put("cyan", AnsiColor.CYAN); + ELEMENTS = Collections.unmodifiableMap(ansiElements); + } + + @Override + protected String transform(ILoggingEvent event, String in) { + AnsiElement element = ELEMENTS.get(getFirstOption()); + if ((event.getMarker() != null && event.getMarker().contains(CRLF_SAFE_MARKER)) || isLoggerSafe(event)) { + return in; + } + String replacement = element == null ? "_" : toAnsiString("_", element); + return in.replaceAll("[\n\r\t]", replacement); + } + + protected boolean isLoggerSafe(ILoggingEvent event) { + for (String safeLogger : SAFE_LOGGERS) { + if (event.getLoggerName().startsWith(safeLogger)) { + return true; + } + } + return false; + } + + protected String toAnsiString(String in, AnsiElement element) { + return AnsiOutput.toString(element, in); + } +} diff --git a/src/main/java/org/jhipster/health/config/CacheConfiguration.java b/src/main/java/org/jhipster/health/config/CacheConfiguration.java new file mode 100644 index 00000000..ea48a39b --- /dev/null +++ b/src/main/java/org/jhipster/health/config/CacheConfiguration.java @@ -0,0 +1,82 @@ +package org.jhipster.health.config; + +import java.time.Duration; +import org.ehcache.config.builders.*; +import org.ehcache.jsr107.Eh107Configuration; +import org.hibernate.cache.jcache.ConfigSettings; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.cache.JCacheManagerCustomizer; +import org.springframework.boot.autoconfigure.orm.jpa.HibernatePropertiesCustomizer; +import org.springframework.boot.info.BuildProperties; +import org.springframework.boot.info.GitProperties; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.cache.interceptor.KeyGenerator; +import org.springframework.context.annotation.*; +import tech.jhipster.config.JHipsterProperties; +import tech.jhipster.config.cache.PrefixedKeyGenerator; + +@Configuration +@EnableCaching +public class CacheConfiguration { + + private GitProperties gitProperties; + private BuildProperties buildProperties; + private final javax.cache.configuration.Configuration jcacheConfiguration; + + public CacheConfiguration(JHipsterProperties jHipsterProperties) { + JHipsterProperties.Cache.Ehcache ehcache = jHipsterProperties.getCache().getEhcache(); + + jcacheConfiguration = + Eh107Configuration.fromEhcacheCacheConfiguration( + CacheConfigurationBuilder + .newCacheConfigurationBuilder(Object.class, Object.class, ResourcePoolsBuilder.heap(ehcache.getMaxEntries())) + .withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofSeconds(ehcache.getTimeToLiveSeconds()))) + .build() + ); + } + + @Bean + public HibernatePropertiesCustomizer hibernatePropertiesCustomizer(javax.cache.CacheManager cacheManager) { + return hibernateProperties -> hibernateProperties.put(ConfigSettings.CACHE_MANAGER, cacheManager); + } + + @Bean + public JCacheManagerCustomizer cacheManagerCustomizer() { + return cm -> { + createCache(cm, org.jhipster.health.repository.UserRepository.USERS_BY_LOGIN_CACHE); + createCache(cm, org.jhipster.health.repository.UserRepository.USERS_BY_EMAIL_CACHE); + createCache(cm, org.jhipster.health.domain.User.class.getName()); + createCache(cm, org.jhipster.health.domain.Authority.class.getName()); + createCache(cm, org.jhipster.health.domain.User.class.getName() + ".authorities"); + createCache(cm, org.jhipster.health.domain.Points.class.getName()); + createCache(cm, org.jhipster.health.domain.Weight.class.getName()); + createCache(cm, org.jhipster.health.domain.BloodPressure.class.getName()); + createCache(cm, org.jhipster.health.domain.Preferences.class.getName()); + // jhipster-needle-ehcache-add-entry + }; + } + + private void createCache(javax.cache.CacheManager cm, String cacheName) { + javax.cache.Cache cache = cm.getCache(cacheName); + if (cache != null) { + cache.clear(); + } else { + cm.createCache(cacheName, jcacheConfiguration); + } + } + + @Autowired(required = false) + public void setGitProperties(GitProperties gitProperties) { + this.gitProperties = gitProperties; + } + + @Autowired(required = false) + public void setBuildProperties(BuildProperties buildProperties) { + this.buildProperties = buildProperties; + } + + @Bean + public KeyGenerator keyGenerator() { + return new PrefixedKeyGenerator(this.gitProperties, this.buildProperties); + } +} diff --git a/src/main/java/org/jhipster/health/config/Constants.java b/src/main/java/org/jhipster/health/config/Constants.java new file mode 100644 index 00000000..5bafde84 --- /dev/null +++ b/src/main/java/org/jhipster/health/config/Constants.java @@ -0,0 +1,15 @@ +package org.jhipster.health.config; + +/** + * Application constants. + */ +public final class Constants { + + // Regex for acceptable logins + public static final String LOGIN_REGEX = "^(?>[a-zA-Z0-9!$&*+=?^_`{|}~.-]+@[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*)|(?>[_.@A-Za-z0-9-]+)$"; + + public static final String SYSTEM = "system"; + public static final String DEFAULT_LANGUAGE = "en"; + + private Constants() {} +} diff --git a/src/main/java/org/jhipster/health/config/DatabaseConfiguration.java b/src/main/java/org/jhipster/health/config/DatabaseConfiguration.java new file mode 100644 index 00000000..37150f43 --- /dev/null +++ b/src/main/java/org/jhipster/health/config/DatabaseConfiguration.java @@ -0,0 +1,59 @@ +package org.jhipster.health.config; + +import java.sql.SQLException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import org.springframework.core.env.Environment; +import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.transaction.annotation.EnableTransactionManagement; +import tech.jhipster.config.JHipsterConstants; +import tech.jhipster.config.h2.H2ConfigurationHelper; + +@Configuration +@EnableJpaRepositories({ "org.jhipster.health.repository" }) +@EnableJpaAuditing(auditorAwareRef = "springSecurityAuditorAware") +@EnableTransactionManagement +@EnableElasticsearchRepositories("org.jhipster.health.repository.search") +public class DatabaseConfiguration { + + private final Logger log = LoggerFactory.getLogger(DatabaseConfiguration.class); + + private final Environment env; + + public DatabaseConfiguration(Environment env) { + this.env = env; + } + + /** + * Open the TCP port for the H2 database, so it is available remotely. + * + * @return the H2 database TCP server. + * @throws SQLException if the server failed to start. + */ + @Bean(initMethod = "start", destroyMethod = "stop") + @Profile(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT) + public Object h2TCPServer() throws SQLException { + String port = getValidPortForH2(); + log.debug("H2 database is available on port {}", port); + return H2ConfigurationHelper.createServer(port); + } + + private String getValidPortForH2() { + int port = Integer.parseInt(env.getProperty("server.port")); + if (port < 10000) { + port = 10000 + port; + } else { + if (port < 63536) { + port = port + 2000; + } else { + port = port - 2000; + } + } + return String.valueOf(port); + } +} diff --git a/src/main/java/org/jhipster/health/config/DateTimeFormatConfiguration.java b/src/main/java/org/jhipster/health/config/DateTimeFormatConfiguration.java new file mode 100644 index 00000000..d78aa423 --- /dev/null +++ b/src/main/java/org/jhipster/health/config/DateTimeFormatConfiguration.java @@ -0,0 +1,20 @@ +package org.jhipster.health.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.format.FormatterRegistry; +import org.springframework.format.datetime.standard.DateTimeFormatterRegistrar; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +/** + * Configure the converters to use the ISO format for dates by default. + */ +@Configuration +public class DateTimeFormatConfiguration implements WebMvcConfigurer { + + @Override + public void addFormatters(FormatterRegistry registry) { + DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar(); + registrar.setUseIsoFormat(true); + registrar.registerFormatters(registry); + } +} diff --git a/src/main/java/org/jhipster/health/config/ElasticsearchConfiguration.java b/src/main/java/org/jhipster/health/config/ElasticsearchConfiguration.java new file mode 100644 index 00000000..c462adbb --- /dev/null +++ b/src/main/java/org/jhipster/health/config/ElasticsearchConfiguration.java @@ -0,0 +1,105 @@ +package org.jhipster.health.config; + +import java.time.Instant; +import java.time.LocalDate; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.util.Arrays; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.convert.converter.Converter; +import org.springframework.data.convert.ReadingConverter; +import org.springframework.data.convert.WritingConverter; +import org.springframework.data.elasticsearch.config.ElasticsearchConfigurationSupport; +import org.springframework.data.elasticsearch.core.convert.ElasticsearchCustomConversions; + +@Configuration +public class ElasticsearchConfiguration extends ElasticsearchConfigurationSupport { + + @Bean + @Override + public ElasticsearchCustomConversions elasticsearchCustomConversions() { + return new ElasticsearchCustomConversions( + Arrays.asList( + new ZonedDateTimeWritingConverter(), + new ZonedDateTimeReadingConverter(), + new InstantWritingConverter(), + new InstantReadingConverter(), + new LocalDateWritingConverter(), + new LocalDateReadingConverter() + ) + ); + } + + @WritingConverter + static class ZonedDateTimeWritingConverter implements Converter { + + @Override + public String convert(ZonedDateTime source) { + if (source == null) { + return null; + } + return source.toInstant().toString(); + } + } + + @ReadingConverter + static class ZonedDateTimeReadingConverter implements Converter { + + @Override + public ZonedDateTime convert(String source) { + if (source == null) { + return null; + } + return Instant.parse(source).atZone(ZoneId.systemDefault()); + } + } + + @WritingConverter + static class InstantWritingConverter implements Converter { + + @Override + public String convert(Instant source) { + if (source == null) { + return null; + } + return source.toString(); + } + } + + @ReadingConverter + static class InstantReadingConverter implements Converter { + + @Override + public Instant convert(String source) { + if (source == null) { + return null; + } + return Instant.parse(source); + } + } + + @WritingConverter + static class LocalDateWritingConverter implements Converter { + + @Override + public String convert(LocalDate source) { + if (source == null) { + return null; + } + return source.toString(); + } + } + + @ReadingConverter + static class LocalDateReadingConverter implements Converter { + + @Override + public LocalDate convert(String source) { + if (source == null) { + return null; + } + return LocalDate.parse(source); + } + } +} diff --git a/src/main/java/org/jhipster/health/config/JacksonConfiguration.java b/src/main/java/org/jhipster/health/config/JacksonConfiguration.java new file mode 100644 index 00000000..78059f8e --- /dev/null +++ b/src/main/java/org/jhipster/health/config/JacksonConfiguration.java @@ -0,0 +1,51 @@ +package org.jhipster.health.config; + +import com.fasterxml.jackson.datatype.hibernate5.Hibernate5Module; +import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.zalando.problem.jackson.ProblemModule; +import org.zalando.problem.violations.ConstraintViolationProblemModule; + +@Configuration +public class JacksonConfiguration { + + /** + * Support for Java date and time API. + * @return the corresponding Jackson module. + */ + @Bean + public JavaTimeModule javaTimeModule() { + return new JavaTimeModule(); + } + + @Bean + public Jdk8Module jdk8TimeModule() { + return new Jdk8Module(); + } + + /* + * Support for Hibernate types in Jackson. + */ + @Bean + public Hibernate5Module hibernate5Module() { + return new Hibernate5Module(); + } + + /* + * Module for serialization/deserialization of RFC7807 Problem. + */ + @Bean + public ProblemModule problemModule() { + return new ProblemModule(); + } + + /* + * Module for serialization/deserialization of ConstraintViolationProblem. + */ + @Bean + public ConstraintViolationProblemModule constraintViolationProblemModule() { + return new ConstraintViolationProblemModule(); + } +} diff --git a/src/main/java/org/jhipster/health/config/LiquibaseConfiguration.java b/src/main/java/org/jhipster/health/config/LiquibaseConfiguration.java new file mode 100644 index 00000000..50f3651c --- /dev/null +++ b/src/main/java/org/jhipster/health/config/LiquibaseConfiguration.java @@ -0,0 +1,69 @@ +package org.jhipster.health.config; + +import java.util.concurrent.Executor; +import javax.sql.DataSource; +import liquibase.integration.spring.SpringLiquibase; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; +import org.springframework.boot.autoconfigure.liquibase.LiquibaseDataSource; +import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; +import org.springframework.core.env.Profiles; +import tech.jhipster.config.JHipsterConstants; +import tech.jhipster.config.liquibase.SpringLiquibaseUtil; + +@Configuration +public class LiquibaseConfiguration { + + private final Logger log = LoggerFactory.getLogger(LiquibaseConfiguration.class); + + private final Environment env; + + public LiquibaseConfiguration(Environment env) { + this.env = env; + } + + @Bean + public SpringLiquibase liquibase( + @Qualifier("taskExecutor") Executor executor, + @LiquibaseDataSource ObjectProvider liquibaseDataSource, + LiquibaseProperties liquibaseProperties, + ObjectProvider dataSource, + DataSourceProperties dataSourceProperties + ) { + // If you don't want Liquibase to start asynchronously, substitute by this: + // SpringLiquibase liquibase = SpringLiquibaseUtil.createSpringLiquibase(liquibaseDataSource.getIfAvailable(), liquibaseProperties, dataSource.getIfUnique(), dataSourceProperties); + SpringLiquibase liquibase = SpringLiquibaseUtil.createAsyncSpringLiquibase( + this.env, + executor, + liquibaseDataSource.getIfAvailable(), + liquibaseProperties, + dataSource.getIfUnique(), + dataSourceProperties + ); + liquibase.setChangeLog("classpath:config/liquibase/master.xml"); + liquibase.setContexts(liquibaseProperties.getContexts()); + liquibase.setDefaultSchema(liquibaseProperties.getDefaultSchema()); + liquibase.setLiquibaseSchema(liquibaseProperties.getLiquibaseSchema()); + liquibase.setLiquibaseTablespace(liquibaseProperties.getLiquibaseTablespace()); + liquibase.setDatabaseChangeLogLockTable(liquibaseProperties.getDatabaseChangeLogLockTable()); + liquibase.setDatabaseChangeLogTable(liquibaseProperties.getDatabaseChangeLogTable()); + liquibase.setDropFirst(liquibaseProperties.isDropFirst()); + liquibase.setLabels(liquibaseProperties.getLabels()); + liquibase.setChangeLogParameters(liquibaseProperties.getParameters()); + liquibase.setRollbackFile(liquibaseProperties.getRollbackFile()); + liquibase.setTestRollbackOnUpdate(liquibaseProperties.isTestRollbackOnUpdate()); + if (env.acceptsProfiles(Profiles.of(JHipsterConstants.SPRING_PROFILE_NO_LIQUIBASE))) { + liquibase.setShouldRun(false); + } else { + liquibase.setShouldRun(liquibaseProperties.isEnabled()); + log.debug("Configuring Liquibase"); + } + return liquibase; + } +} diff --git a/src/main/java/org/jhipster/health/config/LocaleConfiguration.java b/src/main/java/org/jhipster/health/config/LocaleConfiguration.java new file mode 100644 index 00000000..699848d7 --- /dev/null +++ b/src/main/java/org/jhipster/health/config/LocaleConfiguration.java @@ -0,0 +1,26 @@ +package org.jhipster.health.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.LocaleResolver; +import org.springframework.web.servlet.config.annotation.*; +import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; +import tech.jhipster.config.locale.AngularCookieLocaleResolver; + +@Configuration +public class LocaleConfiguration implements WebMvcConfigurer { + + @Bean + public LocaleResolver localeResolver() { + AngularCookieLocaleResolver cookieLocaleResolver = new AngularCookieLocaleResolver(); + cookieLocaleResolver.setCookieName("NG_TRANSLATE_LANG_KEY"); + return cookieLocaleResolver; + } + + @Override + public void addInterceptors(InterceptorRegistry registry) { + LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor(); + localeChangeInterceptor.setParamName("language"); + registry.addInterceptor(localeChangeInterceptor); + } +} diff --git a/src/main/java/org/jhipster/health/config/LoggingAspectConfiguration.java b/src/main/java/org/jhipster/health/config/LoggingAspectConfiguration.java new file mode 100644 index 00000000..641bca4f --- /dev/null +++ b/src/main/java/org/jhipster/health/config/LoggingAspectConfiguration.java @@ -0,0 +1,17 @@ +package org.jhipster.health.config; + +import org.jhipster.health.aop.logging.LoggingAspect; +import org.springframework.context.annotation.*; +import org.springframework.core.env.Environment; +import tech.jhipster.config.JHipsterConstants; + +@Configuration +@EnableAspectJAutoProxy +public class LoggingAspectConfiguration { + + @Bean + @Profile(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT) + public LoggingAspect loggingAspect(Environment env) { + return new LoggingAspect(env); + } +} diff --git a/src/main/java/org/jhipster/health/config/LoggingConfiguration.java b/src/main/java/org/jhipster/health/config/LoggingConfiguration.java new file mode 100644 index 00000000..71b96a84 --- /dev/null +++ b/src/main/java/org/jhipster/health/config/LoggingConfiguration.java @@ -0,0 +1,47 @@ +package org.jhipster.health.config; + +import static tech.jhipster.config.logging.LoggingUtils.*; + +import ch.qos.logback.classic.LoggerContext; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.HashMap; +import java.util.Map; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import tech.jhipster.config.JHipsterProperties; + +/* + * Configures the console and Logstash log appenders from the app properties + */ +@Configuration +public class LoggingConfiguration { + + public LoggingConfiguration( + @Value("${spring.application.name}") String appName, + @Value("${server.port}") String serverPort, + JHipsterProperties jHipsterProperties, + ObjectMapper mapper + ) throws JsonProcessingException { + LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); + + Map map = new HashMap<>(); + map.put("app_name", appName); + map.put("app_port", serverPort); + String customFields = mapper.writeValueAsString(map); + + JHipsterProperties.Logging loggingProperties = jHipsterProperties.getLogging(); + JHipsterProperties.Logging.Logstash logstashProperties = loggingProperties.getLogstash(); + + if (loggingProperties.isUseJsonFormat()) { + addJsonConsoleAppender(context, customFields); + } + if (logstashProperties.isEnabled()) { + addLogstashTcpSocketAppender(context, customFields, logstashProperties); + } + if (loggingProperties.isUseJsonFormat() || logstashProperties.isEnabled()) { + addContextListener(context, customFields, loggingProperties); + } + } +} diff --git a/src/main/java/org/jhipster/health/config/SecurityConfiguration.java b/src/main/java/org/jhipster/health/config/SecurityConfiguration.java new file mode 100644 index 00000000..70a605c3 --- /dev/null +++ b/src/main/java/org/jhipster/health/config/SecurityConfiguration.java @@ -0,0 +1,105 @@ +package org.jhipster.health.config; + +import org.jhipster.health.security.*; +import org.jhipster.health.security.jwt.*; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Import; +import org.springframework.http.HttpMethod; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.http.SessionCreationPolicy; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter; +import org.springframework.web.filter.CorsFilter; +import org.zalando.problem.spring.web.advice.security.SecurityProblemSupport; +import tech.jhipster.config.JHipsterProperties; + +@EnableWebSecurity +@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) +@Import(SecurityProblemSupport.class) +public class SecurityConfiguration { + + private final JHipsterProperties jHipsterProperties; + + private final TokenProvider tokenProvider; + + private final CorsFilter corsFilter; + private final SecurityProblemSupport problemSupport; + + public SecurityConfiguration( + TokenProvider tokenProvider, + CorsFilter corsFilter, + JHipsterProperties jHipsterProperties, + SecurityProblemSupport problemSupport + ) { + this.tokenProvider = tokenProvider; + this.corsFilter = corsFilter; + this.problemSupport = problemSupport; + this.jHipsterProperties = jHipsterProperties; + } + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } + + @Bean + public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { + // @formatter:off + http + .csrf() + .ignoringAntMatchers("/h2-console/**") + .disable() + .addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class) + .exceptionHandling() + .authenticationEntryPoint(problemSupport) + .accessDeniedHandler(problemSupport) + .and() + .headers() + .contentSecurityPolicy(jHipsterProperties.getSecurity().getContentSecurityPolicy()) + .and() + .referrerPolicy(ReferrerPolicyHeaderWriter.ReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN) + .and() + .permissionsPolicy().policy("camera=(), fullscreen=(self), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), midi=(), payment=(), sync-xhr=()") + .and() + .frameOptions().sameOrigin() + .and() + .sessionManagement() + .sessionCreationPolicy(SessionCreationPolicy.STATELESS) + .and() + .authorizeRequests() + .antMatchers(HttpMethod.OPTIONS, "/**").permitAll() + .antMatchers("/app/**/*.{js,html}").permitAll() + .antMatchers("/i18n/**").permitAll() + .antMatchers("/content/**").permitAll() + .antMatchers("/swagger-ui/**").permitAll() + .antMatchers("/test/**").permitAll() + .antMatchers("/h2-console/**").permitAll() + .antMatchers("/api/authenticate").permitAll() + .antMatchers("/api/register").permitAll() + .antMatchers("/api/activate").permitAll() + .antMatchers("/api/account/reset-password/init").permitAll() + .antMatchers("/api/account/reset-password/finish").permitAll() + .antMatchers("/api/admin/**").hasAuthority(AuthoritiesConstants.ADMIN) + .antMatchers("/api/**").authenticated() + .antMatchers("/management/health").permitAll() + .antMatchers("/management/health/**").permitAll() + .antMatchers("/management/info").permitAll() + .antMatchers("/management/prometheus").permitAll() + .antMatchers("/management/**").hasAuthority(AuthoritiesConstants.ADMIN) + .and() + .httpBasic() + .and() + .apply(securityConfigurerAdapter()); + return http.build(); + // @formatter:on + } + + private JWTConfigurer securityConfigurerAdapter() { + return new JWTConfigurer(tokenProvider); + } +} diff --git a/src/main/java/org/jhipster/health/config/StaticResourcesWebConfiguration.java b/src/main/java/org/jhipster/health/config/StaticResourcesWebConfiguration.java new file mode 100644 index 00000000..a16c1470 --- /dev/null +++ b/src/main/java/org/jhipster/health/config/StaticResourcesWebConfiguration.java @@ -0,0 +1,59 @@ +package org.jhipster.health.config; + +import java.util.concurrent.TimeUnit; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import org.springframework.http.CacheControl; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistration; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import tech.jhipster.config.JHipsterConstants; +import tech.jhipster.config.JHipsterProperties; + +@Configuration +@Profile({ JHipsterConstants.SPRING_PROFILE_PRODUCTION }) +public class StaticResourcesWebConfiguration implements WebMvcConfigurer { + + protected static final String[] RESOURCE_LOCATIONS = new String[] { + "classpath:/static/", + "classpath:/static/content/", + "classpath:/static/i18n/", + }; + protected static final String[] RESOURCE_PATHS = new String[] { + "/*.js", + "/*.css", + "/*.svg", + "/*.png", + "*.ico", + "/content/**", + "/i18n/*", + }; + + private final JHipsterProperties jhipsterProperties; + + public StaticResourcesWebConfiguration(JHipsterProperties jHipsterProperties) { + this.jhipsterProperties = jHipsterProperties; + } + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + ResourceHandlerRegistration resourceHandlerRegistration = appendResourceHandler(registry); + initializeResourceHandler(resourceHandlerRegistration); + } + + protected ResourceHandlerRegistration appendResourceHandler(ResourceHandlerRegistry registry) { + return registry.addResourceHandler(RESOURCE_PATHS); + } + + protected void initializeResourceHandler(ResourceHandlerRegistration resourceHandlerRegistration) { + resourceHandlerRegistration.addResourceLocations(RESOURCE_LOCATIONS).setCacheControl(getCacheControl()); + } + + protected CacheControl getCacheControl() { + return CacheControl.maxAge(getJHipsterHttpCacheProperty(), TimeUnit.DAYS).cachePublic(); + } + + private int getJHipsterHttpCacheProperty() { + return jhipsterProperties.getHttp().getCache().getTimeToLiveInDays(); + } +} diff --git a/src/main/java/org/jhipster/health/config/WebConfigurer.java b/src/main/java/org/jhipster/health/config/WebConfigurer.java new file mode 100644 index 00000000..8bc71e0b --- /dev/null +++ b/src/main/java/org/jhipster/health/config/WebConfigurer.java @@ -0,0 +1,111 @@ +package org.jhipster.health.config; + +import static java.net.URLDecoder.decode; + +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.nio.file.Paths; +import javax.servlet.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.web.server.*; +import org.springframework.boot.web.servlet.ServletContextInitializer; +import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; +import org.springframework.core.env.Profiles; +import org.springframework.util.CollectionUtils; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; +import tech.jhipster.config.JHipsterConstants; +import tech.jhipster.config.JHipsterProperties; +import tech.jhipster.config.h2.H2ConfigurationHelper; + +/** + * Configuration of web application with Servlet 3.0 APIs. + */ +@Configuration +public class WebConfigurer implements ServletContextInitializer, WebServerFactoryCustomizer { + + private final Logger log = LoggerFactory.getLogger(WebConfigurer.class); + + private final Environment env; + + private final JHipsterProperties jHipsterProperties; + + public WebConfigurer(Environment env, JHipsterProperties jHipsterProperties) { + this.env = env; + this.jHipsterProperties = jHipsterProperties; + } + + @Override + public void onStartup(ServletContext servletContext) throws ServletException { + if (env.getActiveProfiles().length != 0) { + log.info("Web application configuration, using profiles: {}", (Object[]) env.getActiveProfiles()); + } + + if (env.acceptsProfiles(Profiles.of(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT))) { + initH2Console(servletContext); + } + log.info("Web application fully configured"); + } + + /** + * Customize the Servlet engine: Mime types, the document root, the cache. + */ + @Override + public void customize(WebServerFactory server) { + // When running in an IDE or with ./gradlew bootRun, set location of the static web assets. + setLocationForStaticAssets(server); + } + + private void setLocationForStaticAssets(WebServerFactory server) { + if (server instanceof ConfigurableServletWebServerFactory) { + ConfigurableServletWebServerFactory servletWebServer = (ConfigurableServletWebServerFactory) server; + File root; + String prefixPath = resolvePathPrefix(); + root = new File(prefixPath + "build/resources/main/static/"); + if (root.exists() && root.isDirectory()) { + servletWebServer.setDocumentRoot(root); + } + } + } + + /** + * Resolve path prefix to static resources. + */ + private String resolvePathPrefix() { + String fullExecutablePath = decode(this.getClass().getResource("").getPath(), StandardCharsets.UTF_8); + String rootPath = Paths.get(".").toUri().normalize().getPath(); + String extractedPath = fullExecutablePath.replace(rootPath, ""); + int extractionEndIndex = extractedPath.indexOf("build/"); + if (extractionEndIndex <= 0) { + return ""; + } + return extractedPath.substring(0, extractionEndIndex); + } + + @Bean + public CorsFilter corsFilter() { + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + CorsConfiguration config = jHipsterProperties.getCors(); + if (!CollectionUtils.isEmpty(config.getAllowedOrigins()) || !CollectionUtils.isEmpty(config.getAllowedOriginPatterns())) { + log.debug("Registering CORS filter"); + source.registerCorsConfiguration("/api/**", config); + source.registerCorsConfiguration("/management/**", config); + source.registerCorsConfiguration("/v3/api-docs", config); + source.registerCorsConfiguration("/swagger-ui/**", config); + } + return new CorsFilter(source); + } + + /** + * Initializes H2 console. + */ + private void initH2Console(ServletContext servletContext) { + log.debug("Initialize H2 console"); + H2ConfigurationHelper.initH2Console(servletContext); + } +} diff --git a/src/main/java/org/jhipster/health/config/package-info.java b/src/main/java/org/jhipster/health/config/package-info.java new file mode 100644 index 00000000..05a2884f --- /dev/null +++ b/src/main/java/org/jhipster/health/config/package-info.java @@ -0,0 +1,4 @@ +/** + * Spring Framework configuration files. + */ +package org.jhipster.health.config; diff --git a/src/main/java/org/jhipster/health/domain/AbstractAuditingEntity.java b/src/main/java/org/jhipster/health/domain/AbstractAuditingEntity.java new file mode 100644 index 00000000..7e1004af --- /dev/null +++ b/src/main/java/org/jhipster/health/domain/AbstractAuditingEntity.java @@ -0,0 +1,75 @@ +package org.jhipster.health.domain; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import java.io.Serializable; +import java.time.Instant; +import javax.persistence.Column; +import javax.persistence.EntityListeners; +import javax.persistence.MappedSuperclass; +import org.springframework.data.annotation.CreatedBy; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedBy; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +/** + * Base abstract class for entities which will hold definitions for created, last modified, created by, + * last modified by attributes. + */ +@MappedSuperclass +@EntityListeners(AuditingEntityListener.class) +@JsonIgnoreProperties(value = { "createdBy", "createdDate", "lastModifiedBy", "lastModifiedDate" }, allowGetters = true) +public abstract class AbstractAuditingEntity implements Serializable { + + private static final long serialVersionUID = 1L; + + public abstract T getId(); + + @CreatedBy + @Column(name = "created_by", nullable = false, length = 50, updatable = false) + private String createdBy; + + @CreatedDate + @Column(name = "created_date", updatable = false) + private Instant createdDate = Instant.now(); + + @LastModifiedBy + @Column(name = "last_modified_by", length = 50) + private String lastModifiedBy; + + @LastModifiedDate + @Column(name = "last_modified_date") + private Instant lastModifiedDate = Instant.now(); + + public String getCreatedBy() { + return createdBy; + } + + public void setCreatedBy(String createdBy) { + this.createdBy = createdBy; + } + + public Instant getCreatedDate() { + return createdDate; + } + + public void setCreatedDate(Instant createdDate) { + this.createdDate = createdDate; + } + + public String getLastModifiedBy() { + return lastModifiedBy; + } + + public void setLastModifiedBy(String lastModifiedBy) { + this.lastModifiedBy = lastModifiedBy; + } + + public Instant getLastModifiedDate() { + return lastModifiedDate; + } + + public void setLastModifiedDate(Instant lastModifiedDate) { + this.lastModifiedDate = lastModifiedDate; + } +} diff --git a/src/main/java/org/jhipster/health/domain/Authority.java b/src/main/java/org/jhipster/health/domain/Authority.java new file mode 100644 index 00000000..deae5ff9 --- /dev/null +++ b/src/main/java/org/jhipster/health/domain/Authority.java @@ -0,0 +1,61 @@ +package org.jhipster.health.domain; + +import java.io.Serializable; +import java.util.Objects; +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.Table; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import org.hibernate.annotations.Cache; +import org.hibernate.annotations.CacheConcurrencyStrategy; + +/** + * An authority (a security role) used by Spring Security. + */ +@Entity +@Table(name = "jhi_authority") +@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) +public class Authority implements Serializable { + + private static final long serialVersionUID = 1L; + + @NotNull + @Size(max = 50) + @Id + @Column(length = 50) + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof Authority)) { + return false; + } + return Objects.equals(name, ((Authority) o).name); + } + + @Override + public int hashCode() { + return Objects.hashCode(name); + } + + // prettier-ignore + @Override + public String toString() { + return "Authority{" + + "name='" + name + '\'' + + "}"; + } +} diff --git a/src/main/java/org/jhipster/health/domain/BloodPressure.java b/src/main/java/org/jhipster/health/domain/BloodPressure.java new file mode 100644 index 00000000..5ebf7230 --- /dev/null +++ b/src/main/java/org/jhipster/health/domain/BloodPressure.java @@ -0,0 +1,139 @@ +package org.jhipster.health.domain; + +import java.io.Serializable; +import java.time.ZonedDateTime; +import javax.persistence.*; +import javax.validation.constraints.*; +import org.hibernate.annotations.Cache; +import org.hibernate.annotations.CacheConcurrencyStrategy; + +/** + * A BloodPressure. + */ +@Entity +@Table(name = "blood_pressure") +@Cache(usage = CacheConcurrencyStrategy.READ_WRITE) +@org.springframework.data.elasticsearch.annotations.Document(indexName = "bloodpressure") +@SuppressWarnings("common-java:DuplicatedBlocks") +public class BloodPressure implements Serializable { + + private static final long serialVersionUID = 1L; + + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator") + @SequenceGenerator(name = "sequenceGenerator") + @Column(name = "id") + private Long id; + + @NotNull + @Column(name = "timestamp", nullable = false) + private ZonedDateTime timestamp; + + @NotNull + @Column(name = "systolic", nullable = false) + private Integer systolic; + + @NotNull + @Column(name = "diastolic", nullable = false) + private Integer diastolic; + + @ManyToOne + private User user; + + // jhipster-needle-entity-add-field - JHipster will add fields here + + public Long getId() { + return this.id; + } + + public BloodPressure id(Long id) { + this.setId(id); + return this; + } + + public void setId(Long id) { + this.id = id; + } + + public ZonedDateTime getTimestamp() { + return this.timestamp; + } + + public BloodPressure timestamp(ZonedDateTime timestamp) { + this.setTimestamp(timestamp); + return this; + } + + public void setTimestamp(ZonedDateTime timestamp) { + this.timestamp = timestamp; + } + + public Integer getSystolic() { + return this.systolic; + } + + public BloodPressure systolic(Integer systolic) { + this.setSystolic(systolic); + return this; + } + + public void setSystolic(Integer systolic) { + this.systolic = systolic; + } + + public Integer getDiastolic() { + return this.diastolic; + } + + public BloodPressure diastolic(Integer diastolic) { + this.setDiastolic(diastolic); + return this; + } + + public void setDiastolic(Integer diastolic) { + this.diastolic = diastolic; + } + + public User getUser() { + return this.user; + } + + public void setUser(User user) { + this.user = user; + } + + public BloodPressure user(User user) { + this.setUser(user); + return this; + } + + // jhipster-needle-entity-add-getters-setters - JHipster will add getters and setters here + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof BloodPressure)) { + return false; + } + return id != null && id.equals(((BloodPressure) o).id); + } + + @Override + public int hashCode() { + // see https://vladmihalcea.com/how-to-implement-equals-and-hashcode-using-the-jpa-entity-identifier/ + return getClass().hashCode(); + } + + // prettier-ignore + @Override + public String toString() { + return "BloodPressure{" + + "id=" + getId() + + ", timestamp='" + getTimestamp() + "'" + + ", systolic=" + getSystolic() + + ", diastolic=" + getDiastolic() + + "}"; + } +} diff --git a/src/main/java/org/jhipster/health/domain/Points.java b/src/main/java/org/jhipster/health/domain/Points.java new file mode 100644 index 00000000..ec629b52 --- /dev/null +++ b/src/main/java/org/jhipster/health/domain/Points.java @@ -0,0 +1,172 @@ +package org.jhipster.health.domain; + +import java.io.Serializable; +import java.time.LocalDate; +import javax.persistence.*; +import javax.validation.constraints.*; +import org.hibernate.annotations.Cache; +import org.hibernate.annotations.CacheConcurrencyStrategy; + +/** + * A Points. + */ +@Entity +@Table(name = "points") +@Cache(usage = CacheConcurrencyStrategy.READ_WRITE) +@org.springframework.data.elasticsearch.annotations.Document(indexName = "points") +@SuppressWarnings("common-java:DuplicatedBlocks") +public class Points implements Serializable { + + private static final long serialVersionUID = 1L; + + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator") + @SequenceGenerator(name = "sequenceGenerator") + @Column(name = "id") + private Long id; + + @NotNull + @Column(name = "date", nullable = false) + private LocalDate date; + + @Column(name = "exercise") + private Integer exercise; + + @Column(name = "meals") + private Integer meals; + + @Column(name = "alcohol") + private Integer alcohol; + + @Size(max = 140) + @Column(name = "notes", length = 140) + private String notes; + + @ManyToOne + private User user; + + // jhipster-needle-entity-add-field - JHipster will add fields here + + public Long getId() { + return this.id; + } + + public Points id(Long id) { + this.setId(id); + return this; + } + + public void setId(Long id) { + this.id = id; + } + + public LocalDate getDate() { + return this.date; + } + + public Points date(LocalDate date) { + this.setDate(date); + return this; + } + + public void setDate(LocalDate date) { + this.date = date; + } + + public Integer getExercise() { + return this.exercise; + } + + public Points exercise(Integer exercise) { + this.setExercise(exercise); + return this; + } + + public void setExercise(Integer exercise) { + this.exercise = exercise; + } + + public Integer getMeals() { + return this.meals; + } + + public Points meals(Integer meals) { + this.setMeals(meals); + return this; + } + + public void setMeals(Integer meals) { + this.meals = meals; + } + + public Integer getAlcohol() { + return this.alcohol; + } + + public Points alcohol(Integer alcohol) { + this.setAlcohol(alcohol); + return this; + } + + public void setAlcohol(Integer alcohol) { + this.alcohol = alcohol; + } + + public String getNotes() { + return this.notes; + } + + public Points notes(String notes) { + this.setNotes(notes); + return this; + } + + public void setNotes(String notes) { + this.notes = notes; + } + + public User getUser() { + return this.user; + } + + public void setUser(User user) { + this.user = user; + } + + public Points user(User user) { + this.setUser(user); + return this; + } + + // jhipster-needle-entity-add-getters-setters - JHipster will add getters and setters here + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof Points)) { + return false; + } + return id != null && id.equals(((Points) o).id); + } + + @Override + public int hashCode() { + // see https://vladmihalcea.com/how-to-implement-equals-and-hashcode-using-the-jpa-entity-identifier/ + return getClass().hashCode(); + } + + // prettier-ignore + @Override + public String toString() { + return "Points{" + + "id=" + getId() + + ", date='" + getDate() + "'" + + ", exercise=" + getExercise() + + ", meals=" + getMeals() + + ", alcohol=" + getAlcohol() + + ", notes='" + getNotes() + "'" + + "}"; + } +} diff --git a/src/main/java/org/jhipster/health/domain/Preferences.java b/src/main/java/org/jhipster/health/domain/Preferences.java new file mode 100644 index 00000000..f6c49f1b --- /dev/null +++ b/src/main/java/org/jhipster/health/domain/Preferences.java @@ -0,0 +1,125 @@ +package org.jhipster.health.domain; + +import java.io.Serializable; +import javax.persistence.*; +import javax.validation.constraints.*; +import org.hibernate.annotations.Cache; +import org.hibernate.annotations.CacheConcurrencyStrategy; +import org.jhipster.health.domain.enumeration.Units; + +/** + * A Preferences. + */ +@Entity +@Table(name = "preferences") +@Cache(usage = CacheConcurrencyStrategy.READ_WRITE) +@org.springframework.data.elasticsearch.annotations.Document(indexName = "preferences") +@SuppressWarnings("common-java:DuplicatedBlocks") +public class Preferences implements Serializable { + + private static final long serialVersionUID = 1L; + + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator") + @SequenceGenerator(name = "sequenceGenerator") + @Column(name = "id") + private Long id; + + @NotNull + @Min(value = 10) + @Max(value = 21) + @Column(name = "weekly_goal", nullable = false) + private Integer weeklyGoal; + + @NotNull + @Enumerated(EnumType.STRING) + @Column(name = "weight_units", nullable = false) + private Units weightUnits; + + @OneToOne + @JoinColumn(unique = true) + private User user; + + // jhipster-needle-entity-add-field - JHipster will add fields here + + public Long getId() { + return this.id; + } + + public Preferences id(Long id) { + this.setId(id); + return this; + } + + public void setId(Long id) { + this.id = id; + } + + public Integer getWeeklyGoal() { + return this.weeklyGoal; + } + + public Preferences weeklyGoal(Integer weeklyGoal) { + this.setWeeklyGoal(weeklyGoal); + return this; + } + + public void setWeeklyGoal(Integer weeklyGoal) { + this.weeklyGoal = weeklyGoal; + } + + public Units getWeightUnits() { + return this.weightUnits; + } + + public Preferences weightUnits(Units weightUnits) { + this.setWeightUnits(weightUnits); + return this; + } + + public void setWeightUnits(Units weightUnits) { + this.weightUnits = weightUnits; + } + + public User getUser() { + return this.user; + } + + public void setUser(User user) { + this.user = user; + } + + public Preferences user(User user) { + this.setUser(user); + return this; + } + + // jhipster-needle-entity-add-getters-setters - JHipster will add getters and setters here + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof Preferences)) { + return false; + } + return id != null && id.equals(((Preferences) o).id); + } + + @Override + public int hashCode() { + // see https://vladmihalcea.com/how-to-implement-equals-and-hashcode-using-the-jpa-entity-identifier/ + return getClass().hashCode(); + } + + // prettier-ignore + @Override + public String toString() { + return "Preferences{" + + "id=" + getId() + + ", weeklyGoal=" + getWeeklyGoal() + + ", weightUnits='" + getWeightUnits() + "'" + + "}"; + } +} diff --git a/src/main/java/org/jhipster/health/domain/User.java b/src/main/java/org/jhipster/health/domain/User.java new file mode 100644 index 00000000..465b7959 --- /dev/null +++ b/src/main/java/org/jhipster/health/domain/User.java @@ -0,0 +1,234 @@ +package org.jhipster.health.domain; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import java.io.Serializable; +import java.time.Instant; +import java.util.HashSet; +import java.util.Locale; +import java.util.Set; +import javax.persistence.*; +import javax.validation.constraints.Email; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; +import javax.validation.constraints.Size; +import org.apache.commons.lang3.StringUtils; +import org.hibernate.annotations.BatchSize; +import org.hibernate.annotations.Cache; +import org.hibernate.annotations.CacheConcurrencyStrategy; +import org.jhipster.health.config.Constants; +import org.springframework.data.elasticsearch.annotations.FieldType; + +/** + * A user. + */ +@Entity +@Table(name = "jhi_user") +@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) +@org.springframework.data.elasticsearch.annotations.Document(indexName = "user") +public class User extends AbstractAuditingEntity implements Serializable { + + private static final long serialVersionUID = 1L; + + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator") + @SequenceGenerator(name = "sequenceGenerator") + private Long id; + + @NotNull + @Pattern(regexp = Constants.LOGIN_REGEX) + @Size(min = 1, max = 50) + @Column(length = 50, unique = true, nullable = false) + private String login; + + @JsonIgnore + @NotNull + @Size(min = 60, max = 60) + @Column(name = "password_hash", length = 60, nullable = false) + private String password; + + @Size(max = 50) + @Column(name = "first_name", length = 50) + private String firstName; + + @Size(max = 50) + @Column(name = "last_name", length = 50) + private String lastName; + + @Email + @Size(min = 5, max = 254) + @Column(length = 254, unique = true) + private String email; + + @NotNull + @Column(nullable = false) + private boolean activated = false; + + @Size(min = 2, max = 10) + @Column(name = "lang_key", length = 10) + private String langKey; + + @Size(max = 256) + @Column(name = "image_url", length = 256) + private String imageUrl; + + @Size(max = 20) + @Column(name = "activation_key", length = 20) + @JsonIgnore + private String activationKey; + + @Size(max = 20) + @Column(name = "reset_key", length = 20) + @JsonIgnore + private String resetKey; + + @Column(name = "reset_date") + private Instant resetDate = null; + + @JsonIgnore + @ManyToMany + @JoinTable( + name = "jhi_user_authority", + joinColumns = { @JoinColumn(name = "user_id", referencedColumnName = "id") }, + inverseJoinColumns = { @JoinColumn(name = "authority_name", referencedColumnName = "name") } + ) + @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) + @BatchSize(size = 20) + private Set authorities = new HashSet<>(); + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getLogin() { + return login; + } + + // Lowercase the login before saving it in database + public void setLogin(String login) { + this.login = StringUtils.lowerCase(login, Locale.ENGLISH); + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getImageUrl() { + return imageUrl; + } + + public void setImageUrl(String imageUrl) { + this.imageUrl = imageUrl; + } + + public boolean isActivated() { + return activated; + } + + public void setActivated(boolean activated) { + this.activated = activated; + } + + public String getActivationKey() { + return activationKey; + } + + public void setActivationKey(String activationKey) { + this.activationKey = activationKey; + } + + public String getResetKey() { + return resetKey; + } + + public void setResetKey(String resetKey) { + this.resetKey = resetKey; + } + + public Instant getResetDate() { + return resetDate; + } + + public void setResetDate(Instant resetDate) { + this.resetDate = resetDate; + } + + public String getLangKey() { + return langKey; + } + + public void setLangKey(String langKey) { + this.langKey = langKey; + } + + public Set getAuthorities() { + return authorities; + } + + public void setAuthorities(Set authorities) { + this.authorities = authorities; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof User)) { + return false; + } + return id != null && id.equals(((User) o).id); + } + + @Override + public int hashCode() { + // see https://vladmihalcea.com/how-to-implement-equals-and-hashcode-using-the-jpa-entity-identifier/ + return getClass().hashCode(); + } + + // prettier-ignore + @Override + public String toString() { + return "User{" + + "login='" + login + '\'' + + ", firstName='" + firstName + '\'' + + ", lastName='" + lastName + '\'' + + ", email='" + email + '\'' + + ", imageUrl='" + imageUrl + '\'' + + ", activated='" + activated + '\'' + + ", langKey='" + langKey + '\'' + + ", activationKey='" + activationKey + '\'' + + "}"; + } +} diff --git a/src/main/java/org/jhipster/health/domain/Weight.java b/src/main/java/org/jhipster/health/domain/Weight.java new file mode 100644 index 00000000..96f4f8bb --- /dev/null +++ b/src/main/java/org/jhipster/health/domain/Weight.java @@ -0,0 +1,121 @@ +package org.jhipster.health.domain; + +import java.io.Serializable; +import java.time.ZonedDateTime; +import javax.persistence.*; +import javax.validation.constraints.*; +import org.hibernate.annotations.Cache; +import org.hibernate.annotations.CacheConcurrencyStrategy; + +/** + * A Weight. + */ +@Entity +@Table(name = "weight") +@Cache(usage = CacheConcurrencyStrategy.READ_WRITE) +@org.springframework.data.elasticsearch.annotations.Document(indexName = "weight") +@SuppressWarnings("common-java:DuplicatedBlocks") +public class Weight implements Serializable { + + private static final long serialVersionUID = 1L; + + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator") + @SequenceGenerator(name = "sequenceGenerator") + @Column(name = "id") + private Long id; + + @NotNull + @Column(name = "timestamp", nullable = false) + private ZonedDateTime timestamp; + + @NotNull + @Column(name = "weight", nullable = false) + private Double weight; + + @ManyToOne + private User user; + + // jhipster-needle-entity-add-field - JHipster will add fields here + + public Long getId() { + return this.id; + } + + public Weight id(Long id) { + this.setId(id); + return this; + } + + public void setId(Long id) { + this.id = id; + } + + public ZonedDateTime getTimestamp() { + return this.timestamp; + } + + public Weight timestamp(ZonedDateTime timestamp) { + this.setTimestamp(timestamp); + return this; + } + + public void setTimestamp(ZonedDateTime timestamp) { + this.timestamp = timestamp; + } + + public Double getWeight() { + return this.weight; + } + + public Weight weight(Double weight) { + this.setWeight(weight); + return this; + } + + public void setWeight(Double weight) { + this.weight = weight; + } + + public User getUser() { + return this.user; + } + + public void setUser(User user) { + this.user = user; + } + + public Weight user(User user) { + this.setUser(user); + return this; + } + + // jhipster-needle-entity-add-getters-setters - JHipster will add getters and setters here + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof Weight)) { + return false; + } + return id != null && id.equals(((Weight) o).id); + } + + @Override + public int hashCode() { + // see https://vladmihalcea.com/how-to-implement-equals-and-hashcode-using-the-jpa-entity-identifier/ + return getClass().hashCode(); + } + + // prettier-ignore + @Override + public String toString() { + return "Weight{" + + "id=" + getId() + + ", timestamp='" + getTimestamp() + "'" + + ", weight=" + getWeight() + + "}"; + } +} diff --git a/src/main/java/org/jhipster/health/domain/enumeration/Units.java b/src/main/java/org/jhipster/health/domain/enumeration/Units.java new file mode 100644 index 00000000..bc305667 --- /dev/null +++ b/src/main/java/org/jhipster/health/domain/enumeration/Units.java @@ -0,0 +1,9 @@ +package org.jhipster.health.domain.enumeration; + +/** + * The Units enumeration. + */ +public enum Units { + KG, + LB, +} diff --git a/src/main/java/org/jhipster/health/domain/package-info.java b/src/main/java/org/jhipster/health/domain/package-info.java new file mode 100644 index 00000000..ed27d834 --- /dev/null +++ b/src/main/java/org/jhipster/health/domain/package-info.java @@ -0,0 +1,4 @@ +/** + * JPA domain objects. + */ +package org.jhipster.health.domain; diff --git a/src/main/java/org/jhipster/health/management/SecurityMetersService.java b/src/main/java/org/jhipster/health/management/SecurityMetersService.java new file mode 100644 index 00000000..0789a5b0 --- /dev/null +++ b/src/main/java/org/jhipster/health/management/SecurityMetersService.java @@ -0,0 +1,51 @@ +package org.jhipster.health.management; + +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.MeterRegistry; +import org.springframework.stereotype.Service; + +@Service +public class SecurityMetersService { + + public static final String INVALID_TOKENS_METER_NAME = "security.authentication.invalid-tokens"; + public static final String INVALID_TOKENS_METER_DESCRIPTION = + "Indicates validation error count of the tokens presented by the clients."; + public static final String INVALID_TOKENS_METER_BASE_UNIT = "errors"; + public static final String INVALID_TOKENS_METER_CAUSE_DIMENSION = "cause"; + + private final Counter tokenInvalidSignatureCounter; + private final Counter tokenExpiredCounter; + private final Counter tokenUnsupportedCounter; + private final Counter tokenMalformedCounter; + + public SecurityMetersService(MeterRegistry registry) { + this.tokenInvalidSignatureCounter = invalidTokensCounterForCauseBuilder("invalid-signature").register(registry); + this.tokenExpiredCounter = invalidTokensCounterForCauseBuilder("expired").register(registry); + this.tokenUnsupportedCounter = invalidTokensCounterForCauseBuilder("unsupported").register(registry); + this.tokenMalformedCounter = invalidTokensCounterForCauseBuilder("malformed").register(registry); + } + + private Counter.Builder invalidTokensCounterForCauseBuilder(String cause) { + return Counter + .builder(INVALID_TOKENS_METER_NAME) + .baseUnit(INVALID_TOKENS_METER_BASE_UNIT) + .description(INVALID_TOKENS_METER_DESCRIPTION) + .tag(INVALID_TOKENS_METER_CAUSE_DIMENSION, cause); + } + + public void trackTokenInvalidSignature() { + this.tokenInvalidSignatureCounter.increment(); + } + + public void trackTokenExpired() { + this.tokenExpiredCounter.increment(); + } + + public void trackTokenUnsupported() { + this.tokenUnsupportedCounter.increment(); + } + + public void trackTokenMalformed() { + this.tokenMalformedCounter.increment(); + } +} diff --git a/src/main/java/org/jhipster/health/repository/AuthorityRepository.java b/src/main/java/org/jhipster/health/repository/AuthorityRepository.java new file mode 100644 index 00000000..494d5827 --- /dev/null +++ b/src/main/java/org/jhipster/health/repository/AuthorityRepository.java @@ -0,0 +1,9 @@ +package org.jhipster.health.repository; + +import org.jhipster.health.domain.Authority; +import org.springframework.data.jpa.repository.JpaRepository; + +/** + * Spring Data JPA repository for the {@link Authority} entity. + */ +public interface AuthorityRepository extends JpaRepository {} diff --git a/src/main/java/org/jhipster/health/repository/BloodPressureRepository.java b/src/main/java/org/jhipster/health/repository/BloodPressureRepository.java new file mode 100644 index 00000000..c8d7527e --- /dev/null +++ b/src/main/java/org/jhipster/health/repository/BloodPressureRepository.java @@ -0,0 +1,43 @@ +package org.jhipster.health.repository; + +import java.util.List; +import java.util.Optional; +import org.jhipster.health.domain.BloodPressure; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.*; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +/** + * Spring Data JPA repository for the BloodPressure entity. + */ +@Repository +public interface BloodPressureRepository extends JpaRepository { + @Query("select bloodPressure from BloodPressure bloodPressure where bloodPressure.user.login = ?#{principal.username}") + List findByUserIsCurrentUser(); + + default Optional findOneWithEagerRelationships(Long id) { + return this.findOneWithToOneRelationships(id); + } + + default List findAllWithEagerRelationships() { + return this.findAllWithToOneRelationships(); + } + + default Page findAllWithEagerRelationships(Pageable pageable) { + return this.findAllWithToOneRelationships(pageable); + } + + @Query( + value = "select distinct bloodPressure from BloodPressure bloodPressure left join fetch bloodPressure.user", + countQuery = "select count(distinct bloodPressure) from BloodPressure bloodPressure" + ) + Page findAllWithToOneRelationships(Pageable pageable); + + @Query("select distinct bloodPressure from BloodPressure bloodPressure left join fetch bloodPressure.user") + List findAllWithToOneRelationships(); + + @Query("select bloodPressure from BloodPressure bloodPressure left join fetch bloodPressure.user where bloodPressure.id =:id") + Optional findOneWithToOneRelationships(@Param("id") Long id); +} diff --git a/src/main/java/org/jhipster/health/repository/PointsRepository.java b/src/main/java/org/jhipster/health/repository/PointsRepository.java new file mode 100644 index 00000000..5c1e9ce1 --- /dev/null +++ b/src/main/java/org/jhipster/health/repository/PointsRepository.java @@ -0,0 +1,43 @@ +package org.jhipster.health.repository; + +import java.util.List; +import java.util.Optional; +import org.jhipster.health.domain.Points; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.*; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +/** + * Spring Data JPA repository for the Points entity. + */ +@Repository +public interface PointsRepository extends JpaRepository { + @Query("select points from Points points where points.user.login = ?#{principal.username}") + List findByUserIsCurrentUser(); + + default Optional findOneWithEagerRelationships(Long id) { + return this.findOneWithToOneRelationships(id); + } + + default List findAllWithEagerRelationships() { + return this.findAllWithToOneRelationships(); + } + + default Page findAllWithEagerRelationships(Pageable pageable) { + return this.findAllWithToOneRelationships(pageable); + } + + @Query( + value = "select distinct points from Points points left join fetch points.user", + countQuery = "select count(distinct points) from Points points" + ) + Page findAllWithToOneRelationships(Pageable pageable); + + @Query("select distinct points from Points points left join fetch points.user") + List findAllWithToOneRelationships(); + + @Query("select points from Points points left join fetch points.user where points.id =:id") + Optional findOneWithToOneRelationships(@Param("id") Long id); +} diff --git a/src/main/java/org/jhipster/health/repository/PreferencesRepository.java b/src/main/java/org/jhipster/health/repository/PreferencesRepository.java new file mode 100644 index 00000000..d599c839 --- /dev/null +++ b/src/main/java/org/jhipster/health/repository/PreferencesRepository.java @@ -0,0 +1,40 @@ +package org.jhipster.health.repository; + +import java.util.List; +import java.util.Optional; +import org.jhipster.health.domain.Preferences; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.*; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +/** + * Spring Data JPA repository for the Preferences entity. + */ +@Repository +public interface PreferencesRepository extends JpaRepository { + default Optional findOneWithEagerRelationships(Long id) { + return this.findOneWithToOneRelationships(id); + } + + default List findAllWithEagerRelationships() { + return this.findAllWithToOneRelationships(); + } + + default Page findAllWithEagerRelationships(Pageable pageable) { + return this.findAllWithToOneRelationships(pageable); + } + + @Query( + value = "select distinct preferences from Preferences preferences left join fetch preferences.user", + countQuery = "select count(distinct preferences) from Preferences preferences" + ) + Page findAllWithToOneRelationships(Pageable pageable); + + @Query("select distinct preferences from Preferences preferences left join fetch preferences.user") + List findAllWithToOneRelationships(); + + @Query("select preferences from Preferences preferences left join fetch preferences.user where preferences.id =:id") + Optional findOneWithToOneRelationships(@Param("id") Long id); +} diff --git a/src/main/java/org/jhipster/health/repository/UserRepository.java b/src/main/java/org/jhipster/health/repository/UserRepository.java new file mode 100644 index 00000000..4e8e8285 --- /dev/null +++ b/src/main/java/org/jhipster/health/repository/UserRepository.java @@ -0,0 +1,36 @@ +package org.jhipster.health.repository; + +import java.time.Instant; +import java.util.List; +import java.util.Optional; +import org.jhipster.health.domain.User; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.data.domain.*; +import org.springframework.data.jpa.repository.EntityGraph; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +/** + * Spring Data JPA repository for the {@link User} entity. + */ +@Repository +public interface UserRepository extends JpaRepository { + String USERS_BY_LOGIN_CACHE = "usersByLogin"; + + String USERS_BY_EMAIL_CACHE = "usersByEmail"; + Optional findOneByActivationKey(String activationKey); + List findAllByActivatedIsFalseAndActivationKeyIsNotNullAndCreatedDateBefore(Instant dateTime); + Optional findOneByResetKey(String resetKey); + Optional findOneByEmailIgnoreCase(String email); + Optional findOneByLogin(String login); + + @EntityGraph(attributePaths = "authorities") + @Cacheable(cacheNames = USERS_BY_LOGIN_CACHE) + Optional findOneWithAuthoritiesByLogin(String login); + + @EntityGraph(attributePaths = "authorities") + @Cacheable(cacheNames = USERS_BY_EMAIL_CACHE) + Optional findOneWithAuthoritiesByEmailIgnoreCase(String email); + + Page findAllByIdNotNullAndActivatedIsTrue(Pageable pageable); +} diff --git a/src/main/java/org/jhipster/health/repository/WeightRepository.java b/src/main/java/org/jhipster/health/repository/WeightRepository.java new file mode 100644 index 00000000..0eb0d1b7 --- /dev/null +++ b/src/main/java/org/jhipster/health/repository/WeightRepository.java @@ -0,0 +1,43 @@ +package org.jhipster.health.repository; + +import java.util.List; +import java.util.Optional; +import org.jhipster.health.domain.Weight; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.*; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; + +/** + * Spring Data JPA repository for the Weight entity. + */ +@Repository +public interface WeightRepository extends JpaRepository { + @Query("select weight from Weight weight where weight.user.login = ?#{principal.username}") + List findByUserIsCurrentUser(); + + default Optional findOneWithEagerRelationships(Long id) { + return this.findOneWithToOneRelationships(id); + } + + default List findAllWithEagerRelationships() { + return this.findAllWithToOneRelationships(); + } + + default Page findAllWithEagerRelationships(Pageable pageable) { + return this.findAllWithToOneRelationships(pageable); + } + + @Query( + value = "select distinct weight from Weight weight left join fetch weight.user", + countQuery = "select count(distinct weight) from Weight weight" + ) + Page findAllWithToOneRelationships(Pageable pageable); + + @Query("select distinct weight from Weight weight left join fetch weight.user") + List findAllWithToOneRelationships(); + + @Query("select weight from Weight weight left join fetch weight.user where weight.id =:id") + Optional findOneWithToOneRelationships(@Param("id") Long id); +} diff --git a/src/main/java/org/jhipster/health/repository/package-info.java b/src/main/java/org/jhipster/health/repository/package-info.java new file mode 100644 index 00000000..30270b82 --- /dev/null +++ b/src/main/java/org/jhipster/health/repository/package-info.java @@ -0,0 +1,4 @@ +/** + * Spring Data JPA repositories. + */ +package org.jhipster.health.repository; diff --git a/src/main/java/org/jhipster/health/repository/search/BloodPressureSearchRepository.java b/src/main/java/org/jhipster/health/repository/search/BloodPressureSearchRepository.java new file mode 100644 index 00000000..3c932fdc --- /dev/null +++ b/src/main/java/org/jhipster/health/repository/search/BloodPressureSearchRepository.java @@ -0,0 +1,69 @@ +package org.jhipster.health.repository.search; + +import static org.elasticsearch.index.query.QueryBuilders.queryStringQuery; + +import java.util.List; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.elasticsearch.search.sort.SortBuilder; +import org.jhipster.health.domain.BloodPressure; +import org.jhipster.health.repository.BloodPressureRepository; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate; +import org.springframework.data.elasticsearch.core.SearchHit; +import org.springframework.data.elasticsearch.core.SearchHits; +import org.springframework.data.elasticsearch.core.query.NativeSearchQuery; +import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder; +import org.springframework.data.elasticsearch.core.query.Query; +import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; +import org.springframework.scheduling.annotation.Async; +import org.springframework.transaction.annotation.Isolation; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +/** + * Spring Data Elasticsearch repository for the {@link BloodPressure} entity. + */ +public interface BloodPressureSearchRepository + extends ElasticsearchRepository, BloodPressureSearchRepositoryInternal {} + +interface BloodPressureSearchRepositoryInternal { + Page search(String query, Pageable pageable); + + Page search(Query query); + + void index(BloodPressure entity); +} + +class BloodPressureSearchRepositoryInternalImpl implements BloodPressureSearchRepositoryInternal { + + private final ElasticsearchRestTemplate elasticsearchTemplate; + private final BloodPressureRepository repository; + + BloodPressureSearchRepositoryInternalImpl(ElasticsearchRestTemplate elasticsearchTemplate, BloodPressureRepository repository) { + this.elasticsearchTemplate = elasticsearchTemplate; + this.repository = repository; + } + + @Override + public Page search(String query, Pageable pageable) { + NativeSearchQuery nativeSearchQuery = new NativeSearchQuery(queryStringQuery(query)); + return search(nativeSearchQuery.setPageable(pageable)); + } + + @Override + public Page search(Query query) { + SearchHits searchHits = elasticsearchTemplate.search(query, BloodPressure.class); + List hits = searchHits.map(SearchHit::getContent).stream().collect(Collectors.toList()); + return new PageImpl<>(hits, query.getPageable(), searchHits.getTotalHits()); + } + + @Override + public void index(BloodPressure entity) { + repository.findOneWithEagerRelationships(entity.getId()).ifPresent(elasticsearchTemplate::save); + } +} diff --git a/src/main/java/org/jhipster/health/repository/search/PointsSearchRepository.java b/src/main/java/org/jhipster/health/repository/search/PointsSearchRepository.java new file mode 100644 index 00000000..a9d99d5c --- /dev/null +++ b/src/main/java/org/jhipster/health/repository/search/PointsSearchRepository.java @@ -0,0 +1,68 @@ +package org.jhipster.health.repository.search; + +import static org.elasticsearch.index.query.QueryBuilders.queryStringQuery; + +import java.util.List; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.elasticsearch.search.sort.SortBuilder; +import org.jhipster.health.domain.Points; +import org.jhipster.health.repository.PointsRepository; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate; +import org.springframework.data.elasticsearch.core.SearchHit; +import org.springframework.data.elasticsearch.core.SearchHits; +import org.springframework.data.elasticsearch.core.query.NativeSearchQuery; +import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder; +import org.springframework.data.elasticsearch.core.query.Query; +import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; +import org.springframework.scheduling.annotation.Async; +import org.springframework.transaction.annotation.Isolation; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +/** + * Spring Data Elasticsearch repository for the {@link Points} entity. + */ +public interface PointsSearchRepository extends ElasticsearchRepository, PointsSearchRepositoryInternal {} + +interface PointsSearchRepositoryInternal { + Page search(String query, Pageable pageable); + + Page search(Query query); + + void index(Points entity); +} + +class PointsSearchRepositoryInternalImpl implements PointsSearchRepositoryInternal { + + private final ElasticsearchRestTemplate elasticsearchTemplate; + private final PointsRepository repository; + + PointsSearchRepositoryInternalImpl(ElasticsearchRestTemplate elasticsearchTemplate, PointsRepository repository) { + this.elasticsearchTemplate = elasticsearchTemplate; + this.repository = repository; + } + + @Override + public Page search(String query, Pageable pageable) { + NativeSearchQuery nativeSearchQuery = new NativeSearchQuery(queryStringQuery(query)); + return search(nativeSearchQuery.setPageable(pageable)); + } + + @Override + public Page search(Query query) { + SearchHits searchHits = elasticsearchTemplate.search(query, Points.class); + List hits = searchHits.map(SearchHit::getContent).stream().collect(Collectors.toList()); + return new PageImpl<>(hits, query.getPageable(), searchHits.getTotalHits()); + } + + @Override + public void index(Points entity) { + repository.findOneWithEagerRelationships(entity.getId()).ifPresent(elasticsearchTemplate::save); + } +} diff --git a/src/main/java/org/jhipster/health/repository/search/PreferencesSearchRepository.java b/src/main/java/org/jhipster/health/repository/search/PreferencesSearchRepository.java new file mode 100644 index 00000000..f11a9b1f --- /dev/null +++ b/src/main/java/org/jhipster/health/repository/search/PreferencesSearchRepository.java @@ -0,0 +1,56 @@ +package org.jhipster.health.repository.search; + +import static org.elasticsearch.index.query.QueryBuilders.queryStringQuery; + +import java.util.stream.Stream; +import org.jhipster.health.domain.Preferences; +import org.jhipster.health.repository.PreferencesRepository; +import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate; +import org.springframework.data.elasticsearch.core.SearchHit; +import org.springframework.data.elasticsearch.core.query.NativeSearchQuery; +import org.springframework.data.elasticsearch.core.query.Query; +import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; +import org.springframework.scheduling.annotation.Async; +import org.springframework.transaction.annotation.Isolation; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +/** + * Spring Data Elasticsearch repository for the {@link Preferences} entity. + */ +public interface PreferencesSearchRepository extends ElasticsearchRepository, PreferencesSearchRepositoryInternal {} + +interface PreferencesSearchRepositoryInternal { + Stream search(String query); + + Stream search(Query query); + + void index(Preferences entity); +} + +class PreferencesSearchRepositoryInternalImpl implements PreferencesSearchRepositoryInternal { + + private final ElasticsearchRestTemplate elasticsearchTemplate; + private final PreferencesRepository repository; + + PreferencesSearchRepositoryInternalImpl(ElasticsearchRestTemplate elasticsearchTemplate, PreferencesRepository repository) { + this.elasticsearchTemplate = elasticsearchTemplate; + this.repository = repository; + } + + @Override + public Stream search(String query) { + NativeSearchQuery nativeSearchQuery = new NativeSearchQuery(queryStringQuery(query)); + return search(nativeSearchQuery); + } + + @Override + public Stream search(Query query) { + return elasticsearchTemplate.search(query, Preferences.class).map(SearchHit::getContent).stream(); + } + + @Override + public void index(Preferences entity) { + repository.findOneWithEagerRelationships(entity.getId()).ifPresent(elasticsearchTemplate::save); + } +} diff --git a/src/main/java/org/jhipster/health/repository/search/UserSearchRepository.java b/src/main/java/org/jhipster/health/repository/search/UserSearchRepository.java new file mode 100644 index 00000000..5ff49497 --- /dev/null +++ b/src/main/java/org/jhipster/health/repository/search/UserSearchRepository.java @@ -0,0 +1,34 @@ +package org.jhipster.health.repository.search; + +import static org.elasticsearch.index.query.QueryBuilders.queryStringQuery; + +import java.util.stream.Stream; +import org.jhipster.health.domain.User; +import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate; +import org.springframework.data.elasticsearch.core.SearchHit; +import org.springframework.data.elasticsearch.core.query.NativeSearchQuery; +import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; + +/** + * Spring Data Elasticsearch repository for the User entity. + */ +public interface UserSearchRepository extends ElasticsearchRepository, UserSearchRepositoryInternal {} + +interface UserSearchRepositoryInternal { + Stream search(String query); +} + +class UserSearchRepositoryInternalImpl implements UserSearchRepositoryInternal { + + private final ElasticsearchRestTemplate elasticsearchTemplate; + + UserSearchRepositoryInternalImpl(ElasticsearchRestTemplate elasticsearchTemplate) { + this.elasticsearchTemplate = elasticsearchTemplate; + } + + @Override + public Stream search(String query) { + NativeSearchQuery nativeSearchQuery = new NativeSearchQuery(queryStringQuery(query)); + return elasticsearchTemplate.search(nativeSearchQuery, User.class).map(SearchHit::getContent).stream(); + } +} diff --git a/src/main/java/org/jhipster/health/repository/search/WeightSearchRepository.java b/src/main/java/org/jhipster/health/repository/search/WeightSearchRepository.java new file mode 100644 index 00000000..66901bb2 --- /dev/null +++ b/src/main/java/org/jhipster/health/repository/search/WeightSearchRepository.java @@ -0,0 +1,68 @@ +package org.jhipster.health.repository.search; + +import static org.elasticsearch.index.query.QueryBuilders.queryStringQuery; + +import java.util.List; +import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import org.elasticsearch.search.sort.SortBuilder; +import org.jhipster.health.domain.Weight; +import org.jhipster.health.repository.WeightRepository; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate; +import org.springframework.data.elasticsearch.core.SearchHit; +import org.springframework.data.elasticsearch.core.SearchHits; +import org.springframework.data.elasticsearch.core.query.NativeSearchQuery; +import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder; +import org.springframework.data.elasticsearch.core.query.Query; +import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; +import org.springframework.scheduling.annotation.Async; +import org.springframework.transaction.annotation.Isolation; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; + +/** + * Spring Data Elasticsearch repository for the {@link Weight} entity. + */ +public interface WeightSearchRepository extends ElasticsearchRepository, WeightSearchRepositoryInternal {} + +interface WeightSearchRepositoryInternal { + Page search(String query, Pageable pageable); + + Page search(Query query); + + void index(Weight entity); +} + +class WeightSearchRepositoryInternalImpl implements WeightSearchRepositoryInternal { + + private final ElasticsearchRestTemplate elasticsearchTemplate; + private final WeightRepository repository; + + WeightSearchRepositoryInternalImpl(ElasticsearchRestTemplate elasticsearchTemplate, WeightRepository repository) { + this.elasticsearchTemplate = elasticsearchTemplate; + this.repository = repository; + } + + @Override + public Page search(String query, Pageable pageable) { + NativeSearchQuery nativeSearchQuery = new NativeSearchQuery(queryStringQuery(query)); + return search(nativeSearchQuery.setPageable(pageable)); + } + + @Override + public Page search(Query query) { + SearchHits searchHits = elasticsearchTemplate.search(query, Weight.class); + List hits = searchHits.map(SearchHit::getContent).stream().collect(Collectors.toList()); + return new PageImpl<>(hits, query.getPageable(), searchHits.getTotalHits()); + } + + @Override + public void index(Weight entity) { + repository.findOneWithEagerRelationships(entity.getId()).ifPresent(elasticsearchTemplate::save); + } +} diff --git a/src/main/java/org/jhipster/health/repository/search/package-info.java b/src/main/java/org/jhipster/health/repository/search/package-info.java new file mode 100644 index 00000000..2655491b --- /dev/null +++ b/src/main/java/org/jhipster/health/repository/search/package-info.java @@ -0,0 +1,4 @@ +/** + * Spring Data Elasticsearch repositories. + */ +package org.jhipster.health.repository.search; diff --git a/src/main/java/org/jhipster/health/security/AuthoritiesConstants.java b/src/main/java/org/jhipster/health/security/AuthoritiesConstants.java new file mode 100644 index 00000000..1b21bcc6 --- /dev/null +++ b/src/main/java/org/jhipster/health/security/AuthoritiesConstants.java @@ -0,0 +1,15 @@ +package org.jhipster.health.security; + +/** + * Constants for Spring Security authorities. + */ +public final class AuthoritiesConstants { + + public static final String ADMIN = "ROLE_ADMIN"; + + public static final String USER = "ROLE_USER"; + + public static final String ANONYMOUS = "ROLE_ANONYMOUS"; + + private AuthoritiesConstants() {} +} diff --git a/src/main/java/org/jhipster/health/security/DomainUserDetailsService.java b/src/main/java/org/jhipster/health/security/DomainUserDetailsService.java new file mode 100644 index 00000000..97be3c28 --- /dev/null +++ b/src/main/java/org/jhipster/health/security/DomainUserDetailsService.java @@ -0,0 +1,64 @@ +package org.jhipster.health.security; + +import java.util.*; +import java.util.stream.Collectors; +import org.hibernate.validator.internal.constraintvalidators.hv.EmailValidator; +import org.jhipster.health.domain.Authority; +import org.jhipster.health.domain.User; +import org.jhipster.health.repository.UserRepository; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +/** + * Authenticate a user from the database. + */ +@Component("userDetailsService") +public class DomainUserDetailsService implements UserDetailsService { + + private final Logger log = LoggerFactory.getLogger(DomainUserDetailsService.class); + + private final UserRepository userRepository; + + public DomainUserDetailsService(UserRepository userRepository) { + this.userRepository = userRepository; + } + + @Override + @Transactional(readOnly = true) + public UserDetails loadUserByUsername(final String login) { + log.debug("Authenticating {}", login); + + if (new EmailValidator().isValid(login, null)) { + return userRepository + .findOneWithAuthoritiesByEmailIgnoreCase(login) + .map(user -> createSpringSecurityUser(login, user)) + .orElseThrow(() -> new UsernameNotFoundException("User with email " + login + " was not found in the database")); + } + + String lowercaseLogin = login.toLowerCase(Locale.ENGLISH); + return userRepository + .findOneWithAuthoritiesByLogin(lowercaseLogin) + .map(user -> createSpringSecurityUser(lowercaseLogin, user)) + .orElseThrow(() -> new UsernameNotFoundException("User " + lowercaseLogin + " was not found in the database")); + } + + private org.springframework.security.core.userdetails.User createSpringSecurityUser(String lowercaseLogin, User user) { + if (!user.isActivated()) { + throw new UserNotActivatedException("User " + lowercaseLogin + " was not activated"); + } + List grantedAuthorities = user + .getAuthorities() + .stream() + .map(Authority::getName) + .map(SimpleGrantedAuthority::new) + .collect(Collectors.toList()); + return new org.springframework.security.core.userdetails.User(user.getLogin(), user.getPassword(), grantedAuthorities); + } +} diff --git a/src/main/java/org/jhipster/health/security/SecurityUtils.java b/src/main/java/org/jhipster/health/security/SecurityUtils.java new file mode 100644 index 00000000..cb53c270 --- /dev/null +++ b/src/main/java/org/jhipster/health/security/SecurityUtils.java @@ -0,0 +1,100 @@ +package org.jhipster.health.security; + +import java.util.Arrays; +import java.util.Optional; +import java.util.stream.Stream; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.UserDetails; + +/** + * Utility class for Spring Security. + */ +public final class SecurityUtils { + + private SecurityUtils() {} + + /** + * Get the login of the current user. + * + * @return the login of the current user. + */ + public static Optional getCurrentUserLogin() { + SecurityContext securityContext = SecurityContextHolder.getContext(); + return Optional.ofNullable(extractPrincipal(securityContext.getAuthentication())); + } + + private static String extractPrincipal(Authentication authentication) { + if (authentication == null) { + return null; + } else if (authentication.getPrincipal() instanceof UserDetails) { + UserDetails springSecurityUser = (UserDetails) authentication.getPrincipal(); + return springSecurityUser.getUsername(); + } else if (authentication.getPrincipal() instanceof String) { + return (String) authentication.getPrincipal(); + } + return null; + } + + /** + * Get the JWT of the current user. + * + * @return the JWT of the current user. + */ + public static Optional getCurrentUserJWT() { + SecurityContext securityContext = SecurityContextHolder.getContext(); + return Optional + .ofNullable(securityContext.getAuthentication()) + .filter(authentication -> authentication.getCredentials() instanceof String) + .map(authentication -> (String) authentication.getCredentials()); + } + + /** + * Check if a user is authenticated. + * + * @return true if the user is authenticated, false otherwise. + */ + public static boolean isAuthenticated() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + return authentication != null && getAuthorities(authentication).noneMatch(AuthoritiesConstants.ANONYMOUS::equals); + } + + /** + * Checks if the current user has any of the authorities. + * + * @param authorities the authorities to check. + * @return true if the current user has any of the authorities, false otherwise. + */ + public static boolean hasCurrentUserAnyOfAuthorities(String... authorities) { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + return ( + authentication != null && getAuthorities(authentication).anyMatch(authority -> Arrays.asList(authorities).contains(authority)) + ); + } + + /** + * Checks if the current user has none of the authorities. + * + * @param authorities the authorities to check. + * @return true if the current user has none of the authorities, false otherwise. + */ + public static boolean hasCurrentUserNoneOfAuthorities(String... authorities) { + return !hasCurrentUserAnyOfAuthorities(authorities); + } + + /** + * Checks if the current user has a specific authority. + * + * @param authority the authority to check. + * @return true if the current user has the authority, false otherwise. + */ + public static boolean hasCurrentUserThisAuthority(String authority) { + return hasCurrentUserAnyOfAuthorities(authority); + } + + private static Stream getAuthorities(Authentication authentication) { + return authentication.getAuthorities().stream().map(GrantedAuthority::getAuthority); + } +} diff --git a/src/main/java/org/jhipster/health/security/SpringSecurityAuditorAware.java b/src/main/java/org/jhipster/health/security/SpringSecurityAuditorAware.java new file mode 100644 index 00000000..c5a1f0de --- /dev/null +++ b/src/main/java/org/jhipster/health/security/SpringSecurityAuditorAware.java @@ -0,0 +1,18 @@ +package org.jhipster.health.security; + +import java.util.Optional; +import org.jhipster.health.config.Constants; +import org.springframework.data.domain.AuditorAware; +import org.springframework.stereotype.Component; + +/** + * Implementation of {@link AuditorAware} based on Spring Security. + */ +@Component +public class SpringSecurityAuditorAware implements AuditorAware { + + @Override + public Optional getCurrentAuditor() { + return Optional.of(SecurityUtils.getCurrentUserLogin().orElse(Constants.SYSTEM)); + } +} diff --git a/src/main/java/org/jhipster/health/security/UserNotActivatedException.java b/src/main/java/org/jhipster/health/security/UserNotActivatedException.java new file mode 100644 index 00000000..1f630000 --- /dev/null +++ b/src/main/java/org/jhipster/health/security/UserNotActivatedException.java @@ -0,0 +1,19 @@ +package org.jhipster.health.security; + +import org.springframework.security.core.AuthenticationException; + +/** + * This exception is thrown in case of a not activated user trying to authenticate. + */ +public class UserNotActivatedException extends AuthenticationException { + + private static final long serialVersionUID = 1L; + + public UserNotActivatedException(String message) { + super(message); + } + + public UserNotActivatedException(String message, Throwable t) { + super(message, t); + } +} diff --git a/src/main/java/org/jhipster/health/security/jwt/JWTConfigurer.java b/src/main/java/org/jhipster/health/security/jwt/JWTConfigurer.java new file mode 100644 index 00000000..f604f6e0 --- /dev/null +++ b/src/main/java/org/jhipster/health/security/jwt/JWTConfigurer.java @@ -0,0 +1,21 @@ +package org.jhipster.health.security.jwt; + +import org.springframework.security.config.annotation.SecurityConfigurerAdapter; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.web.DefaultSecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; + +public class JWTConfigurer extends SecurityConfigurerAdapter { + + private final TokenProvider tokenProvider; + + public JWTConfigurer(TokenProvider tokenProvider) { + this.tokenProvider = tokenProvider; + } + + @Override + public void configure(HttpSecurity http) { + JWTFilter customFilter = new JWTFilter(tokenProvider); + http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class); + } +} diff --git a/src/main/java/org/jhipster/health/security/jwt/JWTFilter.java b/src/main/java/org/jhipster/health/security/jwt/JWTFilter.java new file mode 100644 index 00000000..4566b056 --- /dev/null +++ b/src/main/java/org/jhipster/health/security/jwt/JWTFilter.java @@ -0,0 +1,47 @@ +package org.jhipster.health.security.jwt; + +import java.io.IOException; +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.util.StringUtils; +import org.springframework.web.filter.GenericFilterBean; + +/** + * Filters incoming requests and installs a Spring Security principal if a header corresponding to a valid user is + * found. + */ +public class JWTFilter extends GenericFilterBean { + + public static final String AUTHORIZATION_HEADER = "Authorization"; + + private final TokenProvider tokenProvider; + + public JWTFilter(TokenProvider tokenProvider) { + this.tokenProvider = tokenProvider; + } + + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) + throws IOException, ServletException { + HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; + String jwt = resolveToken(httpServletRequest); + if (StringUtils.hasText(jwt) && this.tokenProvider.validateToken(jwt)) { + Authentication authentication = this.tokenProvider.getAuthentication(jwt); + SecurityContextHolder.getContext().setAuthentication(authentication); + } + filterChain.doFilter(servletRequest, servletResponse); + } + + private String resolveToken(HttpServletRequest request) { + String bearerToken = request.getHeader(AUTHORIZATION_HEADER); + if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) { + return bearerToken.substring(7); + } + return null; + } +} diff --git a/src/main/java/org/jhipster/health/security/jwt/TokenProvider.java b/src/main/java/org/jhipster/health/security/jwt/TokenProvider.java new file mode 100644 index 00000000..fdfa9b73 --- /dev/null +++ b/src/main/java/org/jhipster/health/security/jwt/TokenProvider.java @@ -0,0 +1,126 @@ +package org.jhipster.health.security.jwt; + +import io.jsonwebtoken.*; +import io.jsonwebtoken.io.Decoders; +import io.jsonwebtoken.security.Keys; +import io.jsonwebtoken.security.SignatureException; +import java.nio.charset.StandardCharsets; +import java.security.Key; +import java.util.*; +import java.util.stream.Collectors; +import org.jhipster.health.management.SecurityMetersService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.User; +import org.springframework.stereotype.Component; +import org.springframework.util.ObjectUtils; +import tech.jhipster.config.JHipsterProperties; + +@Component +public class TokenProvider { + + private final Logger log = LoggerFactory.getLogger(TokenProvider.class); + + private static final String AUTHORITIES_KEY = "auth"; + + private static final String INVALID_JWT_TOKEN = "Invalid JWT token."; + + private final Key key; + + private final JwtParser jwtParser; + + private final long tokenValidityInMilliseconds; + + private final long tokenValidityInMillisecondsForRememberMe; + + private final SecurityMetersService securityMetersService; + + public TokenProvider(JHipsterProperties jHipsterProperties, SecurityMetersService securityMetersService) { + byte[] keyBytes; + String secret = jHipsterProperties.getSecurity().getAuthentication().getJwt().getBase64Secret(); + if (!ObjectUtils.isEmpty(secret)) { + log.debug("Using a Base64-encoded JWT secret key"); + keyBytes = Decoders.BASE64.decode(secret); + } else { + log.warn( + "Warning: the JWT key used is not Base64-encoded. " + + "We recommend using the `jhipster.security.authentication.jwt.base64-secret` key for optimum security." + ); + secret = jHipsterProperties.getSecurity().getAuthentication().getJwt().getSecret(); + keyBytes = secret.getBytes(StandardCharsets.UTF_8); + } + key = Keys.hmacShaKeyFor(keyBytes); + jwtParser = Jwts.parserBuilder().setSigningKey(key).build(); + this.tokenValidityInMilliseconds = 1000 * jHipsterProperties.getSecurity().getAuthentication().getJwt().getTokenValidityInSeconds(); + this.tokenValidityInMillisecondsForRememberMe = + 1000 * jHipsterProperties.getSecurity().getAuthentication().getJwt().getTokenValidityInSecondsForRememberMe(); + + this.securityMetersService = securityMetersService; + } + + public String createToken(Authentication authentication, boolean rememberMe) { + String authorities = authentication.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.joining(",")); + + long now = (new Date()).getTime(); + Date validity; + if (rememberMe) { + validity = new Date(now + this.tokenValidityInMillisecondsForRememberMe); + } else { + validity = new Date(now + this.tokenValidityInMilliseconds); + } + + return Jwts + .builder() + .setSubject(authentication.getName()) + .claim(AUTHORITIES_KEY, authorities) + .signWith(key, SignatureAlgorithm.HS512) + .setExpiration(validity) + .compact(); + } + + public Authentication getAuthentication(String token) { + Claims claims = jwtParser.parseClaimsJws(token).getBody(); + + Collection authorities = Arrays + .stream(claims.get(AUTHORITIES_KEY).toString().split(",")) + .filter(auth -> !auth.trim().isEmpty()) + .map(SimpleGrantedAuthority::new) + .collect(Collectors.toList()); + + User principal = new User(claims.getSubject(), "", authorities); + + return new UsernamePasswordAuthenticationToken(principal, token, authorities); + } + + public boolean validateToken(String authToken) { + try { + jwtParser.parseClaimsJws(authToken); + + return true; + } catch (ExpiredJwtException e) { + this.securityMetersService.trackTokenExpired(); + + log.trace(INVALID_JWT_TOKEN, e); + } catch (UnsupportedJwtException e) { + this.securityMetersService.trackTokenUnsupported(); + + log.trace(INVALID_JWT_TOKEN, e); + } catch (MalformedJwtException e) { + this.securityMetersService.trackTokenMalformed(); + + log.trace(INVALID_JWT_TOKEN, e); + } catch (SignatureException e) { + this.securityMetersService.trackTokenInvalidSignature(); + + log.trace(INVALID_JWT_TOKEN, e); + } catch (IllegalArgumentException e) { // TODO: should we let it bubble (no catch), to avoid defensive programming and follow the fail-fast principle? + log.error("Token validation error {}", e.getMessage()); + } + + return false; + } +} diff --git a/src/main/java/org/jhipster/health/security/package-info.java b/src/main/java/org/jhipster/health/security/package-info.java new file mode 100644 index 00000000..3eaab12e --- /dev/null +++ b/src/main/java/org/jhipster/health/security/package-info.java @@ -0,0 +1,4 @@ +/** + * Spring Security configuration. + */ +package org.jhipster.health.security; diff --git a/src/main/java/org/jhipster/health/service/ElasticsearchIndexService.java b/src/main/java/org/jhipster/health/service/ElasticsearchIndexService.java new file mode 100644 index 00000000..19d93d12 --- /dev/null +++ b/src/main/java/org/jhipster/health/service/ElasticsearchIndexService.java @@ -0,0 +1,199 @@ +package org.jhipster.health.service; + +import com.codahale.metrics.annotation.Timed; +import com.fasterxml.jackson.annotation.JsonIgnore; +import java.beans.IntrospectionException; +import java.beans.PropertyDescriptor; +import java.io.Serializable; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.stream.Collectors; +import javax.persistence.ManyToMany; +import org.elasticsearch.ResourceAlreadyExistsException; +import org.jhipster.health.config.ApplicationProperties; +import org.jhipster.health.domain.*; +import org.jhipster.health.repository.*; +import org.jhipster.health.repository.search.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.ApplicationListener; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.elasticsearch.core.ElasticsearchOperations; +import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@Transactional(readOnly = true) +public class ElasticsearchIndexService implements ApplicationListener { + + private static final Lock reindexLock = new ReentrantLock(); + + private final Logger log = LoggerFactory.getLogger(ElasticsearchIndexService.class); + + private final PointsRepository pointsRepository; + + private final PointsSearchRepository pointsSearchRepository; + + private final WeightRepository weightRepository; + + private final WeightSearchRepository weightSearchRepository; + + private final BloodPressureRepository bloodPressureRepository; + + private final BloodPressureSearchRepository bloodPressureSearchRepository; + + private final PreferencesRepository preferencesRepository; + + private final PreferencesSearchRepository preferencesSearchRepository; + + private final UserRepository userRepository; + + private final UserSearchRepository userSearchRepository; + + private final ElasticsearchOperations elasticsearchOperations; + + private final ApplicationProperties applicationProperties; + + private boolean reindex_elasticsearch; + + public ElasticsearchIndexService( + UserRepository userRepository, + UserSearchRepository userSearchRepository, + PointsRepository pointsRepository, + PointsSearchRepository pointsSearchRepository, + WeightRepository weightRepository, + WeightSearchRepository weightSearchRepository, + BloodPressureRepository bloodPressureRepository, + BloodPressureSearchRepository bloodPressureSearchRepository, + PreferencesRepository preferencesRepository, + PreferencesSearchRepository preferencesSearchRepository, + ElasticsearchOperations elasticsearchOperations, + ApplicationProperties applicationProperties + ) { + this.userRepository = userRepository; + this.userSearchRepository = userSearchRepository; + this.pointsRepository = pointsRepository; + this.pointsSearchRepository = pointsSearchRepository; + this.weightRepository = weightRepository; + this.weightSearchRepository = weightSearchRepository; + this.bloodPressureRepository = bloodPressureRepository; + this.bloodPressureSearchRepository = bloodPressureSearchRepository; + this.preferencesRepository = preferencesRepository; + this.preferencesSearchRepository = preferencesSearchRepository; + this.elasticsearchOperations = elasticsearchOperations; + this.applicationProperties = applicationProperties; + } + + public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) { + this.reindex_elasticsearch = applicationProperties.getElasticsearch().getReindexOnStartup(); + if (this.reindex_elasticsearch) { + log.info("Reindexing all entities with Elasticsearch..."); + this.reindexSelected(null, true); + } else { + log.info( + "Skipping Elasticsearch reindex. You may reindex all entities with Elasticsearch on start up by setting application.elasticsearch.reindex-on-startup to true." + ); + } + } + + @Async + @Timed + public void reindexSelected(List classesForReindex, boolean all) { + if (reindexLock.tryLock()) { + try { + if (all || classesForReindex.contains("Points")) { + reindexForClass(Points.class, pointsRepository, pointsSearchRepository); + } + if (all || classesForReindex.contains("Weight")) { + reindexForClass(Weight.class, weightRepository, weightSearchRepository); + } + if (all || classesForReindex.contains("BloodPressure")) { + reindexForClass(BloodPressure.class, bloodPressureRepository, bloodPressureSearchRepository); + } + if (all || classesForReindex.contains("Preferences")) { + reindexForClass(Preferences.class, preferencesRepository, preferencesSearchRepository); + } + if (all || classesForReindex.contains("User")) { + reindexForClass(User.class, userRepository, userSearchRepository); + } + + log.info("Elasticsearch: Successfully performed reindexing"); + } finally { + reindexLock.unlock(); + } + } else { + log.info("Elasticsearch: concurrent reindexing attempt"); + } + } + + @SuppressWarnings("unchecked") + private void reindexForClass( + Class entityClass, + JpaRepository jpaRepository, + ElasticsearchRepository elasticsearchRepository + ) { + String className = entityClass.getSimpleName(); + elasticsearchOperations.indexOps(entityClass).delete(); + try { + elasticsearchOperations.indexOps(entityClass).createWithMapping(); + } catch (ResourceAlreadyExistsException e) { + // Do nothing. Index was already concurrently recreated by some other service. + } + if (jpaRepository.count() > 0) { + // if a JHipster entity field is the owner side of a many-to-many relationship, it should be loaded manually + List relationshipGetters = Arrays + .stream(entityClass.getDeclaredFields()) + .filter(field -> field.getType().equals(Set.class)) + .filter(field -> field.getAnnotation(ManyToMany.class) != null) + .filter(field -> field.getAnnotation(ManyToMany.class).mappedBy().isEmpty()) + .filter(field -> field.getAnnotation(JsonIgnore.class) == null) + .map(field -> { + try { + return new PropertyDescriptor(field.getName(), entityClass).getReadMethod(); + } catch (IntrospectionException e) { + log.error( + "Error retrieving getter for class {}, field {}. Field will NOT be indexed", + className, + field.getName(), + e + ); + return null; + } + }) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + + int size = 100; + for (int i = 0; i <= jpaRepository.count() / size; i++) { + Pageable page = PageRequest.of(i, size); + log.info("Indexing {} page {} of {}, size {}", className, i, jpaRepository.count() / size, size); + Page results = jpaRepository.findAll(page); + results.map(result -> { + // if there are any relationships to load, do it now + relationshipGetters.forEach(method -> { + try { + // eagerly load the relationship set + ((Set) method.invoke(result)).size(); + } catch (Exception ex) { + log.error(ex.getMessage()); + } + }); + return result; + }); + elasticsearchRepository.saveAll(results.getContent()); + } + } + log.info("Elasticsearch: Indexed all rows for {}", className); + } +} diff --git a/src/main/java/org/jhipster/health/service/EmailAlreadyUsedException.java b/src/main/java/org/jhipster/health/service/EmailAlreadyUsedException.java new file mode 100644 index 00000000..f813c2f4 --- /dev/null +++ b/src/main/java/org/jhipster/health/service/EmailAlreadyUsedException.java @@ -0,0 +1,10 @@ +package org.jhipster.health.service; + +public class EmailAlreadyUsedException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + public EmailAlreadyUsedException() { + super("Email is already in use!"); + } +} diff --git a/src/main/java/org/jhipster/health/service/InvalidPasswordException.java b/src/main/java/org/jhipster/health/service/InvalidPasswordException.java new file mode 100644 index 00000000..1de2a36e --- /dev/null +++ b/src/main/java/org/jhipster/health/service/InvalidPasswordException.java @@ -0,0 +1,10 @@ +package org.jhipster.health.service; + +public class InvalidPasswordException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + public InvalidPasswordException() { + super("Incorrect password"); + } +} diff --git a/src/main/java/org/jhipster/health/service/MailService.java b/src/main/java/org/jhipster/health/service/MailService.java new file mode 100644 index 00000000..e0454fa4 --- /dev/null +++ b/src/main/java/org/jhipster/health/service/MailService.java @@ -0,0 +1,112 @@ +package org.jhipster.health.service; + +import java.nio.charset.StandardCharsets; +import java.util.Locale; +import javax.mail.MessagingException; +import javax.mail.internet.MimeMessage; +import org.jhipster.health.domain.User; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.MessageSource; +import org.springframework.mail.MailException; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.mail.javamail.MimeMessageHelper; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Service; +import org.thymeleaf.context.Context; +import org.thymeleaf.spring5.SpringTemplateEngine; +import tech.jhipster.config.JHipsterProperties; + +/** + * Service for sending emails. + *

+ * We use the {@link Async} annotation to send emails asynchronously. + */ +@Service +public class MailService { + + private final Logger log = LoggerFactory.getLogger(MailService.class); + + private static final String USER = "user"; + + private static final String BASE_URL = "baseUrl"; + + private final JHipsterProperties jHipsterProperties; + + private final JavaMailSender javaMailSender; + + private final MessageSource messageSource; + + private final SpringTemplateEngine templateEngine; + + public MailService( + JHipsterProperties jHipsterProperties, + JavaMailSender javaMailSender, + MessageSource messageSource, + SpringTemplateEngine templateEngine + ) { + this.jHipsterProperties = jHipsterProperties; + this.javaMailSender = javaMailSender; + this.messageSource = messageSource; + this.templateEngine = templateEngine; + } + + @Async + public void sendEmail(String to, String subject, String content, boolean isMultipart, boolean isHtml) { + log.debug( + "Send email[multipart '{}' and html '{}'] to '{}' with subject '{}' and content={}", + isMultipart, + isHtml, + to, + subject, + content + ); + + // Prepare message using a Spring helper + MimeMessage mimeMessage = javaMailSender.createMimeMessage(); + try { + MimeMessageHelper message = new MimeMessageHelper(mimeMessage, isMultipart, StandardCharsets.UTF_8.name()); + message.setTo(to); + message.setFrom(jHipsterProperties.getMail().getFrom()); + message.setSubject(subject); + message.setText(content, isHtml); + javaMailSender.send(mimeMessage); + log.debug("Sent email to User '{}'", to); + } catch (MailException | MessagingException e) { + log.warn("Email could not be sent to user '{}'", to, e); + } + } + + @Async + public void sendEmailFromTemplate(User user, String templateName, String titleKey) { + if (user.getEmail() == null) { + log.debug("Email doesn't exist for user '{}'", user.getLogin()); + return; + } + Locale locale = Locale.forLanguageTag(user.getLangKey()); + Context context = new Context(locale); + context.setVariable(USER, user); + context.setVariable(BASE_URL, jHipsterProperties.getMail().getBaseUrl()); + String content = templateEngine.process(templateName, context); + String subject = messageSource.getMessage(titleKey, null, locale); + sendEmail(user.getEmail(), subject, content, false, true); + } + + @Async + public void sendActivationEmail(User user) { + log.debug("Sending activation email to '{}'", user.getEmail()); + sendEmailFromTemplate(user, "mail/activationEmail", "email.activation.title"); + } + + @Async + public void sendCreationEmail(User user) { + log.debug("Sending creation email to '{}'", user.getEmail()); + sendEmailFromTemplate(user, "mail/creationEmail", "email.activation.title"); + } + + @Async + public void sendPasswordResetMail(User user) { + log.debug("Sending password reset email to '{}'", user.getEmail()); + sendEmailFromTemplate(user, "mail/passwordResetEmail", "email.reset.title"); + } +} diff --git a/src/main/java/org/jhipster/health/service/UserService.java b/src/main/java/org/jhipster/health/service/UserService.java new file mode 100644 index 00000000..092c505d --- /dev/null +++ b/src/main/java/org/jhipster/health/service/UserService.java @@ -0,0 +1,337 @@ +package org.jhipster.health.service; + +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.*; +import java.util.stream.Collectors; +import org.jhipster.health.config.Constants; +import org.jhipster.health.domain.Authority; +import org.jhipster.health.domain.User; +import org.jhipster.health.repository.AuthorityRepository; +import org.jhipster.health.repository.UserRepository; +import org.jhipster.health.repository.search.UserSearchRepository; +import org.jhipster.health.security.AuthoritiesConstants; +import org.jhipster.health.security.SecurityUtils; +import org.jhipster.health.service.dto.AdminUserDTO; +import org.jhipster.health.service.dto.UserDTO; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.cache.CacheManager; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import tech.jhipster.security.RandomUtil; + +/** + * Service class for managing users. + */ +@Service +@Transactional +public class UserService { + + private final Logger log = LoggerFactory.getLogger(UserService.class); + + private final UserRepository userRepository; + + private final PasswordEncoder passwordEncoder; + + private final UserSearchRepository userSearchRepository; + + private final AuthorityRepository authorityRepository; + + private final CacheManager cacheManager; + + public UserService( + UserRepository userRepository, + PasswordEncoder passwordEncoder, + UserSearchRepository userSearchRepository, + AuthorityRepository authorityRepository, + CacheManager cacheManager + ) { + this.userRepository = userRepository; + this.passwordEncoder = passwordEncoder; + this.userSearchRepository = userSearchRepository; + this.authorityRepository = authorityRepository; + this.cacheManager = cacheManager; + } + + public Optional activateRegistration(String key) { + log.debug("Activating user for activation key {}", key); + return userRepository + .findOneByActivationKey(key) + .map(user -> { + // activate given user for the registration key. + user.setActivated(true); + user.setActivationKey(null); + userSearchRepository.save(user); + this.clearUserCaches(user); + log.debug("Activated user: {}", user); + return user; + }); + } + + public Optional completePasswordReset(String newPassword, String key) { + log.debug("Reset user password for reset key {}", key); + return userRepository + .findOneByResetKey(key) + .filter(user -> user.getResetDate().isAfter(Instant.now().minus(1, ChronoUnit.DAYS))) + .map(user -> { + user.setPassword(passwordEncoder.encode(newPassword)); + user.setResetKey(null); + user.setResetDate(null); + this.clearUserCaches(user); + return user; + }); + } + + public Optional requestPasswordReset(String mail) { + return userRepository + .findOneByEmailIgnoreCase(mail) + .filter(User::isActivated) + .map(user -> { + user.setResetKey(RandomUtil.generateResetKey()); + user.setResetDate(Instant.now()); + this.clearUserCaches(user); + return user; + }); + } + + public User registerUser(AdminUserDTO userDTO, String password) { + userRepository + .findOneByLogin(userDTO.getLogin().toLowerCase()) + .ifPresent(existingUser -> { + boolean removed = removeNonActivatedUser(existingUser); + if (!removed) { + throw new UsernameAlreadyUsedException(); + } + }); + userRepository + .findOneByEmailIgnoreCase(userDTO.getEmail()) + .ifPresent(existingUser -> { + boolean removed = removeNonActivatedUser(existingUser); + if (!removed) { + throw new EmailAlreadyUsedException(); + } + }); + User newUser = new User(); + String encryptedPassword = passwordEncoder.encode(password); + newUser.setLogin(userDTO.getLogin().toLowerCase()); + // new user gets initially a generated password + newUser.setPassword(encryptedPassword); + newUser.setFirstName(userDTO.getFirstName()); + newUser.setLastName(userDTO.getLastName()); + if (userDTO.getEmail() != null) { + newUser.setEmail(userDTO.getEmail().toLowerCase()); + } + newUser.setImageUrl(userDTO.getImageUrl()); + newUser.setLangKey(userDTO.getLangKey()); + // new user is not active + newUser.setActivated(false); + // new user gets registration key + newUser.setActivationKey(RandomUtil.generateActivationKey()); + Set authorities = new HashSet<>(); + authorityRepository.findById(AuthoritiesConstants.USER).ifPresent(authorities::add); + newUser.setAuthorities(authorities); + userRepository.save(newUser); + userSearchRepository.save(newUser); + this.clearUserCaches(newUser); + log.debug("Created Information for User: {}", newUser); + return newUser; + } + + private boolean removeNonActivatedUser(User existingUser) { + if (existingUser.isActivated()) { + return false; + } + userRepository.delete(existingUser); + userRepository.flush(); + this.clearUserCaches(existingUser); + return true; + } + + public User createUser(AdminUserDTO userDTO) { + User user = new User(); + user.setLogin(userDTO.getLogin().toLowerCase()); + user.setFirstName(userDTO.getFirstName()); + user.setLastName(userDTO.getLastName()); + if (userDTO.getEmail() != null) { + user.setEmail(userDTO.getEmail().toLowerCase()); + } + user.setImageUrl(userDTO.getImageUrl()); + if (userDTO.getLangKey() == null) { + user.setLangKey(Constants.DEFAULT_LANGUAGE); // default language + } else { + user.setLangKey(userDTO.getLangKey()); + } + String encryptedPassword = passwordEncoder.encode(RandomUtil.generatePassword()); + user.setPassword(encryptedPassword); + user.setResetKey(RandomUtil.generateResetKey()); + user.setResetDate(Instant.now()); + user.setActivated(true); + if (userDTO.getAuthorities() != null) { + Set authorities = userDTO + .getAuthorities() + .stream() + .map(authorityRepository::findById) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(Collectors.toSet()); + user.setAuthorities(authorities); + } + userRepository.save(user); + userSearchRepository.save(user); + this.clearUserCaches(user); + log.debug("Created Information for User: {}", user); + return user; + } + + /** + * Update all information for a specific user, and return the modified user. + * + * @param userDTO user to update. + * @return updated user. + */ + public Optional updateUser(AdminUserDTO userDTO) { + return Optional + .of(userRepository.findById(userDTO.getId())) + .filter(Optional::isPresent) + .map(Optional::get) + .map(user -> { + this.clearUserCaches(user); + user.setLogin(userDTO.getLogin().toLowerCase()); + user.setFirstName(userDTO.getFirstName()); + user.setLastName(userDTO.getLastName()); + if (userDTO.getEmail() != null) { + user.setEmail(userDTO.getEmail().toLowerCase()); + } + user.setImageUrl(userDTO.getImageUrl()); + user.setActivated(userDTO.isActivated()); + user.setLangKey(userDTO.getLangKey()); + Set managedAuthorities = user.getAuthorities(); + managedAuthorities.clear(); + userDTO + .getAuthorities() + .stream() + .map(authorityRepository::findById) + .filter(Optional::isPresent) + .map(Optional::get) + .forEach(managedAuthorities::add); + userSearchRepository.save(user); + this.clearUserCaches(user); + log.debug("Changed Information for User: {}", user); + return user; + }) + .map(AdminUserDTO::new); + } + + public void deleteUser(String login) { + userRepository + .findOneByLogin(login) + .ifPresent(user -> { + userRepository.delete(user); + userSearchRepository.delete(user); + this.clearUserCaches(user); + log.debug("Deleted User: {}", user); + }); + } + + /** + * Update basic information (first name, last name, email, language) for the current user. + * + * @param firstName first name of user. + * @param lastName last name of user. + * @param email email id of user. + * @param langKey language key. + * @param imageUrl image URL of user. + */ + public void updateUser(String firstName, String lastName, String email, String langKey, String imageUrl) { + SecurityUtils + .getCurrentUserLogin() + .flatMap(userRepository::findOneByLogin) + .ifPresent(user -> { + user.setFirstName(firstName); + user.setLastName(lastName); + if (email != null) { + user.setEmail(email.toLowerCase()); + } + user.setLangKey(langKey); + user.setImageUrl(imageUrl); + userSearchRepository.save(user); + this.clearUserCaches(user); + log.debug("Changed Information for User: {}", user); + }); + } + + @Transactional + public void changePassword(String currentClearTextPassword, String newPassword) { + SecurityUtils + .getCurrentUserLogin() + .flatMap(userRepository::findOneByLogin) + .ifPresent(user -> { + String currentEncryptedPassword = user.getPassword(); + if (!passwordEncoder.matches(currentClearTextPassword, currentEncryptedPassword)) { + throw new InvalidPasswordException(); + } + String encryptedPassword = passwordEncoder.encode(newPassword); + user.setPassword(encryptedPassword); + this.clearUserCaches(user); + log.debug("Changed password for User: {}", user); + }); + } + + @Transactional(readOnly = true) + public Page getAllManagedUsers(Pageable pageable) { + return userRepository.findAll(pageable).map(AdminUserDTO::new); + } + + @Transactional(readOnly = true) + public Page getAllPublicUsers(Pageable pageable) { + return userRepository.findAllByIdNotNullAndActivatedIsTrue(pageable).map(UserDTO::new); + } + + @Transactional(readOnly = true) + public Optional getUserWithAuthoritiesByLogin(String login) { + return userRepository.findOneWithAuthoritiesByLogin(login); + } + + @Transactional(readOnly = true) + public Optional getUserWithAuthorities() { + return SecurityUtils.getCurrentUserLogin().flatMap(userRepository::findOneWithAuthoritiesByLogin); + } + + /** + * Not activated users should be automatically deleted after 3 days. + *

+ * This is scheduled to get fired everyday, at 01:00 (am). + */ + @Scheduled(cron = "0 0 1 * * ?") + public void removeNotActivatedUsers() { + userRepository + .findAllByActivatedIsFalseAndActivationKeyIsNotNullAndCreatedDateBefore(Instant.now().minus(3, ChronoUnit.DAYS)) + .forEach(user -> { + log.debug("Deleting not activated user {}", user.getLogin()); + userRepository.delete(user); + userSearchRepository.delete(user); + this.clearUserCaches(user); + }); + } + + /** + * Gets a list of all the authorities. + * @return a list of all the authorities. + */ + @Transactional(readOnly = true) + public List getAuthorities() { + return authorityRepository.findAll().stream().map(Authority::getName).collect(Collectors.toList()); + } + + private void clearUserCaches(User user) { + Objects.requireNonNull(cacheManager.getCache(UserRepository.USERS_BY_LOGIN_CACHE)).evict(user.getLogin()); + if (user.getEmail() != null) { + Objects.requireNonNull(cacheManager.getCache(UserRepository.USERS_BY_EMAIL_CACHE)).evict(user.getEmail()); + } + } +} diff --git a/src/main/java/org/jhipster/health/service/UsernameAlreadyUsedException.java b/src/main/java/org/jhipster/health/service/UsernameAlreadyUsedException.java new file mode 100644 index 00000000..9b0c4424 --- /dev/null +++ b/src/main/java/org/jhipster/health/service/UsernameAlreadyUsedException.java @@ -0,0 +1,10 @@ +package org.jhipster.health.service; + +public class UsernameAlreadyUsedException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + public UsernameAlreadyUsedException() { + super("Login name already used!"); + } +} diff --git a/src/main/java/org/jhipster/health/service/dto/AdminUserDTO.java b/src/main/java/org/jhipster/health/service/dto/AdminUserDTO.java new file mode 100644 index 00000000..b457662e --- /dev/null +++ b/src/main/java/org/jhipster/health/service/dto/AdminUserDTO.java @@ -0,0 +1,196 @@ +package org.jhipster.health.service.dto; + +import java.io.Serializable; +import java.time.Instant; +import java.util.Set; +import java.util.stream.Collectors; +import javax.validation.constraints.*; +import org.jhipster.health.config.Constants; +import org.jhipster.health.domain.Authority; +import org.jhipster.health.domain.User; + +/** + * A DTO representing a user, with his authorities. + */ +public class AdminUserDTO implements Serializable { + + private static final long serialVersionUID = 1L; + + private Long id; + + @NotBlank + @Pattern(regexp = Constants.LOGIN_REGEX) + @Size(min = 1, max = 50) + private String login; + + @Size(max = 50) + private String firstName; + + @Size(max = 50) + private String lastName; + + @Email + @Size(min = 5, max = 254) + private String email; + + @Size(max = 256) + private String imageUrl; + + private boolean activated = false; + + @Size(min = 2, max = 10) + private String langKey; + + private String createdBy; + + private Instant createdDate; + + private String lastModifiedBy; + + private Instant lastModifiedDate; + + private Set authorities; + + public AdminUserDTO() { + // Empty constructor needed for Jackson. + } + + public AdminUserDTO(User user) { + this.id = user.getId(); + this.login = user.getLogin(); + this.firstName = user.getFirstName(); + this.lastName = user.getLastName(); + this.email = user.getEmail(); + this.activated = user.isActivated(); + this.imageUrl = user.getImageUrl(); + this.langKey = user.getLangKey(); + this.createdBy = user.getCreatedBy(); + this.createdDate = user.getCreatedDate(); + this.lastModifiedBy = user.getLastModifiedBy(); + this.lastModifiedDate = user.getLastModifiedDate(); + this.authorities = user.getAuthorities().stream().map(Authority::getName).collect(Collectors.toSet()); + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getLogin() { + return login; + } + + public void setLogin(String login) { + this.login = login; + } + + public String getFirstName() { + return firstName; + } + + public void setFirstName(String firstName) { + this.firstName = firstName; + } + + public String getLastName() { + return lastName; + } + + public void setLastName(String lastName) { + this.lastName = lastName; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getImageUrl() { + return imageUrl; + } + + public void setImageUrl(String imageUrl) { + this.imageUrl = imageUrl; + } + + public boolean isActivated() { + return activated; + } + + public void setActivated(boolean activated) { + this.activated = activated; + } + + public String getLangKey() { + return langKey; + } + + public void setLangKey(String langKey) { + this.langKey = langKey; + } + + public String getCreatedBy() { + return createdBy; + } + + public void setCreatedBy(String createdBy) { + this.createdBy = createdBy; + } + + public Instant getCreatedDate() { + return createdDate; + } + + public void setCreatedDate(Instant createdDate) { + this.createdDate = createdDate; + } + + public String getLastModifiedBy() { + return lastModifiedBy; + } + + public void setLastModifiedBy(String lastModifiedBy) { + this.lastModifiedBy = lastModifiedBy; + } + + public Instant getLastModifiedDate() { + return lastModifiedDate; + } + + public void setLastModifiedDate(Instant lastModifiedDate) { + this.lastModifiedDate = lastModifiedDate; + } + + public Set getAuthorities() { + return authorities; + } + + public void setAuthorities(Set authorities) { + this.authorities = authorities; + } + + // prettier-ignore + @Override + public String toString() { + return "AdminUserDTO{" + + "login='" + login + '\'' + + ", firstName='" + firstName + '\'' + + ", lastName='" + lastName + '\'' + + ", email='" + email + '\'' + + ", imageUrl='" + imageUrl + '\'' + + ", activated=" + activated + + ", langKey='" + langKey + '\'' + + ", createdBy=" + createdBy + + ", createdDate=" + createdDate + + ", lastModifiedBy='" + lastModifiedBy + '\'' + + ", lastModifiedDate=" + lastModifiedDate + + ", authorities=" + authorities + + "}"; + } +} diff --git a/src/main/java/org/jhipster/health/service/dto/PasswordChangeDTO.java b/src/main/java/org/jhipster/health/service/dto/PasswordChangeDTO.java new file mode 100644 index 00000000..a7b171b0 --- /dev/null +++ b/src/main/java/org/jhipster/health/service/dto/PasswordChangeDTO.java @@ -0,0 +1,39 @@ +package org.jhipster.health.service.dto; + +import java.io.Serializable; + +/** + * A DTO representing a password change required data - current and new password. + */ +public class PasswordChangeDTO implements Serializable { + + private static final long serialVersionUID = 1L; + + private String currentPassword; + private String newPassword; + + public PasswordChangeDTO() { + // Empty constructor needed for Jackson. + } + + public PasswordChangeDTO(String currentPassword, String newPassword) { + this.currentPassword = currentPassword; + this.newPassword = newPassword; + } + + public String getCurrentPassword() { + return currentPassword; + } + + public void setCurrentPassword(String currentPassword) { + this.currentPassword = currentPassword; + } + + public String getNewPassword() { + return newPassword; + } + + public void setNewPassword(String newPassword) { + this.newPassword = newPassword; + } +} diff --git a/src/main/java/org/jhipster/health/service/dto/UserDTO.java b/src/main/java/org/jhipster/health/service/dto/UserDTO.java new file mode 100644 index 00000000..c1ab9c6f --- /dev/null +++ b/src/main/java/org/jhipster/health/service/dto/UserDTO.java @@ -0,0 +1,51 @@ +package org.jhipster.health.service.dto; + +import java.io.Serializable; +import org.jhipster.health.domain.User; + +/** + * A DTO representing a user, with only the public attributes. + */ +public class UserDTO implements Serializable { + + private static final long serialVersionUID = 1L; + + private Long id; + + private String login; + + public UserDTO() { + // Empty constructor needed for Jackson. + } + + public UserDTO(User user) { + this.id = user.getId(); + // Customize it here if you need, or not, firstName/lastName/etc + this.login = user.getLogin(); + } + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getLogin() { + return login; + } + + public void setLogin(String login) { + this.login = login; + } + + // prettier-ignore + @Override + public String toString() { + return "UserDTO{" + + "id='" + id + '\'' + + ", login='" + login + '\'' + + "}"; + } +} diff --git a/src/main/java/org/jhipster/health/service/dto/package-info.java b/src/main/java/org/jhipster/health/service/dto/package-info.java new file mode 100644 index 00000000..5f15b131 --- /dev/null +++ b/src/main/java/org/jhipster/health/service/dto/package-info.java @@ -0,0 +1,4 @@ +/** + * Data Transfer Objects. + */ +package org.jhipster.health.service.dto; diff --git a/src/main/java/org/jhipster/health/service/mapper/UserMapper.java b/src/main/java/org/jhipster/health/service/mapper/UserMapper.java new file mode 100644 index 00000000..a8a63030 --- /dev/null +++ b/src/main/java/org/jhipster/health/service/mapper/UserMapper.java @@ -0,0 +1,147 @@ +package org.jhipster.health.service.mapper; + +import java.util.*; +import java.util.stream.Collectors; +import org.jhipster.health.domain.Authority; +import org.jhipster.health.domain.User; +import org.jhipster.health.service.dto.AdminUserDTO; +import org.jhipster.health.service.dto.UserDTO; +import org.mapstruct.BeanMapping; +import org.mapstruct.Mapping; +import org.mapstruct.Named; +import org.springframework.stereotype.Service; + +/** + * Mapper for the entity {@link User} and its DTO called {@link UserDTO}. + * + * Normal mappers are generated using MapStruct, this one is hand-coded as MapStruct + * support is still in beta, and requires a manual step with an IDE. + */ +@Service +public class UserMapper { + + public List usersToUserDTOs(List users) { + return users.stream().filter(Objects::nonNull).map(this::userToUserDTO).collect(Collectors.toList()); + } + + public UserDTO userToUserDTO(User user) { + return new UserDTO(user); + } + + public List usersToAdminUserDTOs(List users) { + return users.stream().filter(Objects::nonNull).map(this::userToAdminUserDTO).collect(Collectors.toList()); + } + + public AdminUserDTO userToAdminUserDTO(User user) { + return new AdminUserDTO(user); + } + + public List userDTOsToUsers(List userDTOs) { + return userDTOs.stream().filter(Objects::nonNull).map(this::userDTOToUser).collect(Collectors.toList()); + } + + public User userDTOToUser(AdminUserDTO userDTO) { + if (userDTO == null) { + return null; + } else { + User user = new User(); + user.setId(userDTO.getId()); + user.setLogin(userDTO.getLogin()); + user.setFirstName(userDTO.getFirstName()); + user.setLastName(userDTO.getLastName()); + user.setEmail(userDTO.getEmail()); + user.setImageUrl(userDTO.getImageUrl()); + user.setActivated(userDTO.isActivated()); + user.setLangKey(userDTO.getLangKey()); + Set authorities = this.authoritiesFromStrings(userDTO.getAuthorities()); + user.setAuthorities(authorities); + return user; + } + } + + private Set authoritiesFromStrings(Set authoritiesAsString) { + Set authorities = new HashSet<>(); + + if (authoritiesAsString != null) { + authorities = + authoritiesAsString + .stream() + .map(string -> { + Authority auth = new Authority(); + auth.setName(string); + return auth; + }) + .collect(Collectors.toSet()); + } + + return authorities; + } + + public User userFromId(Long id) { + if (id == null) { + return null; + } + User user = new User(); + user.setId(id); + return user; + } + + @Named("id") + @BeanMapping(ignoreByDefault = true) + @Mapping(target = "id", source = "id") + public UserDTO toDtoId(User user) { + if (user == null) { + return null; + } + UserDTO userDto = new UserDTO(); + userDto.setId(user.getId()); + return userDto; + } + + @Named("idSet") + @BeanMapping(ignoreByDefault = true) + @Mapping(target = "id", source = "id") + public Set toDtoIdSet(Set users) { + if (users == null) { + return Collections.emptySet(); + } + + Set userSet = new HashSet<>(); + for (User userEntity : users) { + userSet.add(this.toDtoId(userEntity)); + } + + return userSet; + } + + @Named("login") + @BeanMapping(ignoreByDefault = true) + @Mapping(target = "id", source = "id") + @Mapping(target = "login", source = "login") + public UserDTO toDtoLogin(User user) { + if (user == null) { + return null; + } + UserDTO userDto = new UserDTO(); + userDto.setId(user.getId()); + userDto.setLogin(user.getLogin()); + return userDto; + } + + @Named("loginSet") + @BeanMapping(ignoreByDefault = true) + @Mapping(target = "id", source = "id") + @Mapping(target = "login", source = "login") + public Set toDtoLoginSet(Set users) { + if (users == null) { + return Collections.emptySet(); + } + + Set userSet = new HashSet<>(); + for (User userEntity : users) { + userSet.add(this.toDtoLogin(userEntity)); + } + + return userSet; + } +} diff --git a/src/main/java/org/jhipster/health/service/mapper/package-info.java b/src/main/java/org/jhipster/health/service/mapper/package-info.java new file mode 100644 index 00000000..b971ff47 --- /dev/null +++ b/src/main/java/org/jhipster/health/service/mapper/package-info.java @@ -0,0 +1,4 @@ +/** + * MapStruct mappers for mapping domain objects and Data Transfer Objects. + */ +package org.jhipster.health.service.mapper; diff --git a/src/main/java/org/jhipster/health/service/package-info.java b/src/main/java/org/jhipster/health/service/package-info.java new file mode 100644 index 00000000..8aa1c8a5 --- /dev/null +++ b/src/main/java/org/jhipster/health/service/package-info.java @@ -0,0 +1,4 @@ +/** + * Service layer beans. + */ +package org.jhipster.health.service; diff --git a/src/main/java/org/jhipster/health/web/rest/AccountResource.java b/src/main/java/org/jhipster/health/web/rest/AccountResource.java new file mode 100644 index 00000000..d50d6eb8 --- /dev/null +++ b/src/main/java/org/jhipster/health/web/rest/AccountResource.java @@ -0,0 +1,194 @@ +package org.jhipster.health.web.rest; + +import java.util.*; +import javax.servlet.http.HttpServletRequest; +import javax.validation.Valid; +import org.apache.commons.lang3.StringUtils; +import org.jhipster.health.domain.User; +import org.jhipster.health.repository.UserRepository; +import org.jhipster.health.security.SecurityUtils; +import org.jhipster.health.service.MailService; +import org.jhipster.health.service.UserService; +import org.jhipster.health.service.dto.AdminUserDTO; +import org.jhipster.health.service.dto.PasswordChangeDTO; +import org.jhipster.health.web.rest.errors.*; +import org.jhipster.health.web.rest.vm.KeyAndPasswordVM; +import org.jhipster.health.web.rest.vm.ManagedUserVM; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.*; + +/** + * REST controller for managing the current user's account. + */ +@RestController +@RequestMapping("/api") +public class AccountResource { + + private static class AccountResourceException extends RuntimeException { + + private AccountResourceException(String message) { + super(message); + } + } + + private final Logger log = LoggerFactory.getLogger(AccountResource.class); + + private final UserRepository userRepository; + + private final UserService userService; + + private final MailService mailService; + + public AccountResource(UserRepository userRepository, UserService userService, MailService mailService) { + this.userRepository = userRepository; + this.userService = userService; + this.mailService = mailService; + } + + /** + * {@code POST /register} : register the user. + * + * @param managedUserVM the managed user View Model. + * @throws InvalidPasswordException {@code 400 (Bad Request)} if the password is incorrect. + * @throws EmailAlreadyUsedException {@code 400 (Bad Request)} if the email is already used. + * @throws LoginAlreadyUsedException {@code 400 (Bad Request)} if the login is already used. + */ + @PostMapping("/register") + @ResponseStatus(HttpStatus.CREATED) + public void registerAccount(@Valid @RequestBody ManagedUserVM managedUserVM) { + if (isPasswordLengthInvalid(managedUserVM.getPassword())) { + throw new InvalidPasswordException(); + } + User user = userService.registerUser(managedUserVM, managedUserVM.getPassword()); + mailService.sendActivationEmail(user); + } + + /** + * {@code GET /activate} : activate the registered user. + * + * @param key the activation key. + * @throws RuntimeException {@code 500 (Internal Server Error)} if the user couldn't be activated. + */ + @GetMapping("/activate") + public void activateAccount(@RequestParam(value = "key") String key) { + Optional user = userService.activateRegistration(key); + if (!user.isPresent()) { + throw new AccountResourceException("No user was found for this activation key"); + } + } + + /** + * {@code GET /authenticate} : check if the user is authenticated, and return its login. + * + * @param request the HTTP request. + * @return the login if the user is authenticated. + */ + @GetMapping("/authenticate") + public String isAuthenticated(HttpServletRequest request) { + log.debug("REST request to check if the current user is authenticated"); + return request.getRemoteUser(); + } + + /** + * {@code GET /account} : get the current user. + * + * @return the current user. + * @throws RuntimeException {@code 500 (Internal Server Error)} if the user couldn't be returned. + */ + @GetMapping("/account") + public AdminUserDTO getAccount() { + return userService + .getUserWithAuthorities() + .map(AdminUserDTO::new) + .orElseThrow(() -> new AccountResourceException("User could not be found")); + } + + /** + * {@code POST /account} : update the current user information. + * + * @param userDTO the current user information. + * @throws EmailAlreadyUsedException {@code 400 (Bad Request)} if the email is already used. + * @throws RuntimeException {@code 500 (Internal Server Error)} if the user login wasn't found. + */ + @PostMapping("/account") + public void saveAccount(@Valid @RequestBody AdminUserDTO userDTO) { + String userLogin = SecurityUtils + .getCurrentUserLogin() + .orElseThrow(() -> new AccountResourceException("Current user login not found")); + Optional existingUser = userRepository.findOneByEmailIgnoreCase(userDTO.getEmail()); + if (existingUser.isPresent() && (!existingUser.get().getLogin().equalsIgnoreCase(userLogin))) { + throw new EmailAlreadyUsedException(); + } + Optional user = userRepository.findOneByLogin(userLogin); + if (!user.isPresent()) { + throw new AccountResourceException("User could not be found"); + } + userService.updateUser( + userDTO.getFirstName(), + userDTO.getLastName(), + userDTO.getEmail(), + userDTO.getLangKey(), + userDTO.getImageUrl() + ); + } + + /** + * {@code POST /account/change-password} : changes the current user's password. + * + * @param passwordChangeDto current and new password. + * @throws InvalidPasswordException {@code 400 (Bad Request)} if the new password is incorrect. + */ + @PostMapping(path = "/account/change-password") + public void changePassword(@RequestBody PasswordChangeDTO passwordChangeDto) { + if (isPasswordLengthInvalid(passwordChangeDto.getNewPassword())) { + throw new InvalidPasswordException(); + } + userService.changePassword(passwordChangeDto.getCurrentPassword(), passwordChangeDto.getNewPassword()); + } + + /** + * {@code POST /account/reset-password/init} : Send an email to reset the password of the user. + * + * @param mail the mail of the user. + */ + @PostMapping(path = "/account/reset-password/init") + public void requestPasswordReset(@RequestBody String mail) { + Optional user = userService.requestPasswordReset(mail); + if (user.isPresent()) { + mailService.sendPasswordResetMail(user.get()); + } else { + // Pretend the request has been successful to prevent checking which emails really exist + // but log that an invalid attempt has been made + log.warn("Password reset requested for non existing mail"); + } + } + + /** + * {@code POST /account/reset-password/finish} : Finish to reset the password of the user. + * + * @param keyAndPassword the generated key and the new password. + * @throws InvalidPasswordException {@code 400 (Bad Request)} if the password is incorrect. + * @throws RuntimeException {@code 500 (Internal Server Error)} if the password could not be reset. + */ + @PostMapping(path = "/account/reset-password/finish") + public void finishPasswordReset(@RequestBody KeyAndPasswordVM keyAndPassword) { + if (isPasswordLengthInvalid(keyAndPassword.getNewPassword())) { + throw new InvalidPasswordException(); + } + Optional user = userService.completePasswordReset(keyAndPassword.getNewPassword(), keyAndPassword.getKey()); + + if (!user.isPresent()) { + throw new AccountResourceException("No user was found for this reset key"); + } + } + + private static boolean isPasswordLengthInvalid(String password) { + return ( + StringUtils.isEmpty(password) || + password.length() < ManagedUserVM.PASSWORD_MIN_LENGTH || + password.length() > ManagedUserVM.PASSWORD_MAX_LENGTH + ); + } +} diff --git a/src/main/java/org/jhipster/health/web/rest/BloodPressureResource.java b/src/main/java/org/jhipster/health/web/rest/BloodPressureResource.java new file mode 100644 index 00000000..84d15a92 --- /dev/null +++ b/src/main/java/org/jhipster/health/web/rest/BloodPressureResource.java @@ -0,0 +1,243 @@ +package org.jhipster.health.web.rest; + +import static org.elasticsearch.index.query.QueryBuilders.*; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import org.jhipster.health.domain.BloodPressure; +import org.jhipster.health.repository.BloodPressureRepository; +import org.jhipster.health.repository.search.BloodPressureSearchRepository; +import org.jhipster.health.web.rest.errors.BadRequestAlertException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; +import tech.jhipster.web.util.HeaderUtil; +import tech.jhipster.web.util.PaginationUtil; +import tech.jhipster.web.util.ResponseUtil; + +/** + * REST controller for managing {@link org.jhipster.health.domain.BloodPressure}. + */ +@RestController +@RequestMapping("/api") +@Transactional +public class BloodPressureResource { + + private final Logger log = LoggerFactory.getLogger(BloodPressureResource.class); + + private static final String ENTITY_NAME = "bloodPressure"; + + @Value("${jhipster.clientApp.name}") + private String applicationName; + + private final BloodPressureRepository bloodPressureRepository; + + private final BloodPressureSearchRepository bloodPressureSearchRepository; + + public BloodPressureResource( + BloodPressureRepository bloodPressureRepository, + BloodPressureSearchRepository bloodPressureSearchRepository + ) { + this.bloodPressureRepository = bloodPressureRepository; + this.bloodPressureSearchRepository = bloodPressureSearchRepository; + } + + /** + * {@code POST /blood-pressures} : Create a new bloodPressure. + * + * @param bloodPressure the bloodPressure to create. + * @return the {@link ResponseEntity} with status {@code 201 (Created)} and with body the new bloodPressure, or with status {@code 400 (Bad Request)} if the bloodPressure has already an ID. + * @throws URISyntaxException if the Location URI syntax is incorrect. + */ + @PostMapping("/blood-pressures") + public ResponseEntity createBloodPressure(@Valid @RequestBody BloodPressure bloodPressure) throws URISyntaxException { + log.debug("REST request to save BloodPressure : {}", bloodPressure); + if (bloodPressure.getId() != null) { + throw new BadRequestAlertException("A new bloodPressure cannot already have an ID", ENTITY_NAME, "idexists"); + } + BloodPressure result = bloodPressureRepository.save(bloodPressure); + bloodPressureSearchRepository.index(result); + return ResponseEntity + .created(new URI("/api/blood-pressures/" + result.getId())) + .headers(HeaderUtil.createEntityCreationAlert(applicationName, true, ENTITY_NAME, result.getId().toString())) + .body(result); + } + + /** + * {@code PUT /blood-pressures/:id} : Updates an existing bloodPressure. + * + * @param id the id of the bloodPressure to save. + * @param bloodPressure the bloodPressure to update. + * @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body the updated bloodPressure, + * or with status {@code 400 (Bad Request)} if the bloodPressure is not valid, + * or with status {@code 500 (Internal Server Error)} if the bloodPressure couldn't be updated. + * @throws URISyntaxException if the Location URI syntax is incorrect. + */ + @PutMapping("/blood-pressures/{id}") + public ResponseEntity updateBloodPressure( + @PathVariable(value = "id", required = false) final Long id, + @Valid @RequestBody BloodPressure bloodPressure + ) throws URISyntaxException { + log.debug("REST request to update BloodPressure : {}, {}", id, bloodPressure); + if (bloodPressure.getId() == null) { + throw new BadRequestAlertException("Invalid id", ENTITY_NAME, "idnull"); + } + if (!Objects.equals(id, bloodPressure.getId())) { + throw new BadRequestAlertException("Invalid ID", ENTITY_NAME, "idinvalid"); + } + + if (!bloodPressureRepository.existsById(id)) { + throw new BadRequestAlertException("Entity not found", ENTITY_NAME, "idnotfound"); + } + + BloodPressure result = bloodPressureRepository.save(bloodPressure); + bloodPressureSearchRepository.index(result); + return ResponseEntity + .ok() + .headers(HeaderUtil.createEntityUpdateAlert(applicationName, true, ENTITY_NAME, bloodPressure.getId().toString())) + .body(result); + } + + /** + * {@code PATCH /blood-pressures/:id} : Partial updates given fields of an existing bloodPressure, field will ignore if it is null + * + * @param id the id of the bloodPressure to save. + * @param bloodPressure the bloodPressure to update. + * @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body the updated bloodPressure, + * or with status {@code 400 (Bad Request)} if the bloodPressure is not valid, + * or with status {@code 404 (Not Found)} if the bloodPressure is not found, + * or with status {@code 500 (Internal Server Error)} if the bloodPressure couldn't be updated. + * @throws URISyntaxException if the Location URI syntax is incorrect. + */ + @PatchMapping(value = "/blood-pressures/{id}", consumes = { "application/json", "application/merge-patch+json" }) + public ResponseEntity partialUpdateBloodPressure( + @PathVariable(value = "id", required = false) final Long id, + @NotNull @RequestBody BloodPressure bloodPressure + ) throws URISyntaxException { + log.debug("REST request to partial update BloodPressure partially : {}, {}", id, bloodPressure); + if (bloodPressure.getId() == null) { + throw new BadRequestAlertException("Invalid id", ENTITY_NAME, "idnull"); + } + if (!Objects.equals(id, bloodPressure.getId())) { + throw new BadRequestAlertException("Invalid ID", ENTITY_NAME, "idinvalid"); + } + + if (!bloodPressureRepository.existsById(id)) { + throw new BadRequestAlertException("Entity not found", ENTITY_NAME, "idnotfound"); + } + + Optional result = bloodPressureRepository + .findById(bloodPressure.getId()) + .map(existingBloodPressure -> { + if (bloodPressure.getTimestamp() != null) { + existingBloodPressure.setTimestamp(bloodPressure.getTimestamp()); + } + if (bloodPressure.getSystolic() != null) { + existingBloodPressure.setSystolic(bloodPressure.getSystolic()); + } + if (bloodPressure.getDiastolic() != null) { + existingBloodPressure.setDiastolic(bloodPressure.getDiastolic()); + } + + return existingBloodPressure; + }) + .map(bloodPressureRepository::save) + .map(savedBloodPressure -> { + bloodPressureSearchRepository.save(savedBloodPressure); + + return savedBloodPressure; + }); + + return ResponseUtil.wrapOrNotFound( + result, + HeaderUtil.createEntityUpdateAlert(applicationName, true, ENTITY_NAME, bloodPressure.getId().toString()) + ); + } + + /** + * {@code GET /blood-pressures} : get all the bloodPressures. + * + * @param pageable the pagination information. + * @param eagerload flag to eager load entities from relationships (This is applicable for many-to-many). + * @return the {@link ResponseEntity} with status {@code 200 (OK)} and the list of bloodPressures in body. + */ + @GetMapping("/blood-pressures") + public ResponseEntity> getAllBloodPressures( + @org.springdoc.api.annotations.ParameterObject Pageable pageable, + @RequestParam(required = false, defaultValue = "false") boolean eagerload + ) { + log.debug("REST request to get a page of BloodPressures"); + Page page; + if (eagerload) { + page = bloodPressureRepository.findAllWithEagerRelationships(pageable); + } else { + page = bloodPressureRepository.findAll(pageable); + } + HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(ServletUriComponentsBuilder.fromCurrentRequest(), page); + return ResponseEntity.ok().headers(headers).body(page.getContent()); + } + + /** + * {@code GET /blood-pressures/:id} : get the "id" bloodPressure. + * + * @param id the id of the bloodPressure to retrieve. + * @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body the bloodPressure, or with status {@code 404 (Not Found)}. + */ + @GetMapping("/blood-pressures/{id}") + public ResponseEntity getBloodPressure(@PathVariable Long id) { + log.debug("REST request to get BloodPressure : {}", id); + Optional bloodPressure = bloodPressureRepository.findOneWithEagerRelationships(id); + return ResponseUtil.wrapOrNotFound(bloodPressure); + } + + /** + * {@code DELETE /blood-pressures/:id} : delete the "id" bloodPressure. + * + * @param id the id of the bloodPressure to delete. + * @return the {@link ResponseEntity} with status {@code 204 (NO_CONTENT)}. + */ + @DeleteMapping("/blood-pressures/{id}") + public ResponseEntity deleteBloodPressure(@PathVariable Long id) { + log.debug("REST request to delete BloodPressure : {}", id); + bloodPressureRepository.deleteById(id); + bloodPressureSearchRepository.deleteById(id); + return ResponseEntity + .noContent() + .headers(HeaderUtil.createEntityDeletionAlert(applicationName, true, ENTITY_NAME, id.toString())) + .build(); + } + + /** + * {@code SEARCH /_search/blood-pressures?query=:query} : search for the bloodPressure corresponding + * to the query. + * + * @param query the query of the bloodPressure search. + * @param pageable the pagination information. + * @return the result of the search. + */ + @GetMapping("/_search/blood-pressures") + public ResponseEntity> searchBloodPressures( + @RequestParam String query, + @org.springdoc.api.annotations.ParameterObject Pageable pageable + ) { + log.debug("REST request to search for a page of BloodPressures for query {}", query); + Page page = bloodPressureSearchRepository.search(query, pageable); + HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(ServletUriComponentsBuilder.fromCurrentRequest(), page); + return ResponseEntity.ok().headers(headers).body(page.getContent()); + } +} diff --git a/src/main/java/org/jhipster/health/web/rest/ClientForwardController.java b/src/main/java/org/jhipster/health/web/rest/ClientForwardController.java new file mode 100644 index 00000000..02c5b825 --- /dev/null +++ b/src/main/java/org/jhipster/health/web/rest/ClientForwardController.java @@ -0,0 +1,17 @@ +package org.jhipster.health.web.rest; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; + +@Controller +public class ClientForwardController { + + /** + * Forwards any unmapped paths (except those containing a period) to the client {@code index.html}. + * @return forward to client {@code index.html}. + */ + @GetMapping(value = "/**/{path:[^\\.]*}") + public String forward() { + return "forward:/"; + } +} diff --git a/src/main/java/org/jhipster/health/web/rest/ElasticsearchIndexResource.java b/src/main/java/org/jhipster/health/web/rest/ElasticsearchIndexResource.java new file mode 100644 index 00000000..d0567091 --- /dev/null +++ b/src/main/java/org/jhipster/health/web/rest/ElasticsearchIndexResource.java @@ -0,0 +1,64 @@ +package org.jhipster.health.web.rest; + +import com.codahale.metrics.annotation.Timed; +import java.net.URISyntaxException; +import java.util.List; +import org.jhipster.health.security.AuthoritiesConstants; +import org.jhipster.health.security.SecurityUtils; +import org.jhipster.health.service.ElasticsearchIndexService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.annotation.Secured; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; +import tech.jhipster.web.util.HeaderUtil; + +/** + * REST controller for managing Elasticsearch index. + */ +@RestController +@RequestMapping("/api") +public class ElasticsearchIndexResource { + + private final Logger log = LoggerFactory.getLogger(ElasticsearchIndexResource.class); + + private final ElasticsearchIndexService elasticsearchIndexService; + + public ElasticsearchIndexResource(ElasticsearchIndexService elasticsearchIndexService) { + this.elasticsearchIndexService = elasticsearchIndexService; + } + + @Value("${jhipster.clientApp.name}") + private String applicationName; + + /** + * POST /elasticsearch/index -> Reindex all Elasticsearch documents + */ + @PostMapping("/elasticsearch/index") + @Timed + @Secured(AuthoritiesConstants.ADMIN) + public ResponseEntity reindexAll() throws URISyntaxException { + log.info("REST request to reindex Elasticsearch by user : {}, all entities", SecurityUtils.getCurrentUserLogin()); + elasticsearchIndexService.reindexSelected(null, true); + return ResponseEntity.accepted().headers(HeaderUtil.createAlert(applicationName, "elasticsearch.reindex.accepted", "")).build(); + } + + /** + * POST /elasticsearch/selected -> Reindex selected Elasticsearch documents + */ + @PostMapping("/elasticsearch/selected") + @Timed + @Secured(AuthoritiesConstants.ADMIN) + public ResponseEntity reindexSelected(@RequestBody List selectedEntities) throws URISyntaxException { + log.info("REST request to reindex Elasticsearch by user : {}, entities: {}", SecurityUtils.getCurrentUserLogin(), selectedEntities); + elasticsearchIndexService.reindexSelected(selectedEntities, false); + return ResponseEntity + .accepted() + .headers(HeaderUtil.createAlert(applicationName, "elasticsearch.reindex.acceptedSelected", "")) + .build(); + } +} diff --git a/src/main/java/org/jhipster/health/web/rest/PointsResource.java b/src/main/java/org/jhipster/health/web/rest/PointsResource.java new file mode 100644 index 00000000..cd02e632 --- /dev/null +++ b/src/main/java/org/jhipster/health/web/rest/PointsResource.java @@ -0,0 +1,246 @@ +package org.jhipster.health.web.rest; + +import static org.elasticsearch.index.query.QueryBuilders.*; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import org.jhipster.health.domain.Points; +import org.jhipster.health.repository.PointsRepository; +import org.jhipster.health.repository.search.PointsSearchRepository; +import org.jhipster.health.web.rest.errors.BadRequestAlertException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; +import tech.jhipster.web.util.HeaderUtil; +import tech.jhipster.web.util.PaginationUtil; +import tech.jhipster.web.util.ResponseUtil; + +/** + * REST controller for managing {@link org.jhipster.health.domain.Points}. + */ +@RestController +@RequestMapping("/api") +@Transactional +public class PointsResource { + + private final Logger log = LoggerFactory.getLogger(PointsResource.class); + + private static final String ENTITY_NAME = "points"; + + @Value("${jhipster.clientApp.name}") + private String applicationName; + + private final PointsRepository pointsRepository; + + private final PointsSearchRepository pointsSearchRepository; + + public PointsResource(PointsRepository pointsRepository, PointsSearchRepository pointsSearchRepository) { + this.pointsRepository = pointsRepository; + this.pointsSearchRepository = pointsSearchRepository; + } + + /** + * {@code POST /points} : Create a new points. + * + * @param points the points to create. + * @return the {@link ResponseEntity} with status {@code 201 (Created)} and with body the new points, or with status {@code 400 (Bad Request)} if the points has already an ID. + * @throws URISyntaxException if the Location URI syntax is incorrect. + */ + @PostMapping("/points") + public ResponseEntity createPoints(@Valid @RequestBody Points points) throws URISyntaxException { + log.debug("REST request to save Points : {}", points); + if (points.getId() != null) { + throw new BadRequestAlertException("A new points cannot already have an ID", ENTITY_NAME, "idexists"); + } + Points result = pointsRepository.save(points); + pointsSearchRepository.index(result); + return ResponseEntity + .created(new URI("/api/points/" + result.getId())) + .headers(HeaderUtil.createEntityCreationAlert(applicationName, true, ENTITY_NAME, result.getId().toString())) + .body(result); + } + + /** + * {@code PUT /points/:id} : Updates an existing points. + * + * @param id the id of the points to save. + * @param points the points to update. + * @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body the updated points, + * or with status {@code 400 (Bad Request)} if the points is not valid, + * or with status {@code 500 (Internal Server Error)} if the points couldn't be updated. + * @throws URISyntaxException if the Location URI syntax is incorrect. + */ + @PutMapping("/points/{id}") + public ResponseEntity updatePoints( + @PathVariable(value = "id", required = false) final Long id, + @Valid @RequestBody Points points + ) throws URISyntaxException { + log.debug("REST request to update Points : {}, {}", id, points); + if (points.getId() == null) { + throw new BadRequestAlertException("Invalid id", ENTITY_NAME, "idnull"); + } + if (!Objects.equals(id, points.getId())) { + throw new BadRequestAlertException("Invalid ID", ENTITY_NAME, "idinvalid"); + } + + if (!pointsRepository.existsById(id)) { + throw new BadRequestAlertException("Entity not found", ENTITY_NAME, "idnotfound"); + } + + Points result = pointsRepository.save(points); + pointsSearchRepository.index(result); + return ResponseEntity + .ok() + .headers(HeaderUtil.createEntityUpdateAlert(applicationName, true, ENTITY_NAME, points.getId().toString())) + .body(result); + } + + /** + * {@code PATCH /points/:id} : Partial updates given fields of an existing points, field will ignore if it is null + * + * @param id the id of the points to save. + * @param points the points to update. + * @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body the updated points, + * or with status {@code 400 (Bad Request)} if the points is not valid, + * or with status {@code 404 (Not Found)} if the points is not found, + * or with status {@code 500 (Internal Server Error)} if the points couldn't be updated. + * @throws URISyntaxException if the Location URI syntax is incorrect. + */ + @PatchMapping(value = "/points/{id}", consumes = { "application/json", "application/merge-patch+json" }) + public ResponseEntity partialUpdatePoints( + @PathVariable(value = "id", required = false) final Long id, + @NotNull @RequestBody Points points + ) throws URISyntaxException { + log.debug("REST request to partial update Points partially : {}, {}", id, points); + if (points.getId() == null) { + throw new BadRequestAlertException("Invalid id", ENTITY_NAME, "idnull"); + } + if (!Objects.equals(id, points.getId())) { + throw new BadRequestAlertException("Invalid ID", ENTITY_NAME, "idinvalid"); + } + + if (!pointsRepository.existsById(id)) { + throw new BadRequestAlertException("Entity not found", ENTITY_NAME, "idnotfound"); + } + + Optional result = pointsRepository + .findById(points.getId()) + .map(existingPoints -> { + if (points.getDate() != null) { + existingPoints.setDate(points.getDate()); + } + if (points.getExercise() != null) { + existingPoints.setExercise(points.getExercise()); + } + if (points.getMeals() != null) { + existingPoints.setMeals(points.getMeals()); + } + if (points.getAlcohol() != null) { + existingPoints.setAlcohol(points.getAlcohol()); + } + if (points.getNotes() != null) { + existingPoints.setNotes(points.getNotes()); + } + + return existingPoints; + }) + .map(pointsRepository::save) + .map(savedPoints -> { + pointsSearchRepository.save(savedPoints); + + return savedPoints; + }); + + return ResponseUtil.wrapOrNotFound( + result, + HeaderUtil.createEntityUpdateAlert(applicationName, true, ENTITY_NAME, points.getId().toString()) + ); + } + + /** + * {@code GET /points} : get all the points. + * + * @param pageable the pagination information. + * @param eagerload flag to eager load entities from relationships (This is applicable for many-to-many). + * @return the {@link ResponseEntity} with status {@code 200 (OK)} and the list of points in body. + */ + @GetMapping("/points") + public ResponseEntity> getAllPoints( + @org.springdoc.api.annotations.ParameterObject Pageable pageable, + @RequestParam(required = false, defaultValue = "false") boolean eagerload + ) { + log.debug("REST request to get a page of Points"); + Page page; + if (eagerload) { + page = pointsRepository.findAllWithEagerRelationships(pageable); + } else { + page = pointsRepository.findAll(pageable); + } + HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(ServletUriComponentsBuilder.fromCurrentRequest(), page); + return ResponseEntity.ok().headers(headers).body(page.getContent()); + } + + /** + * {@code GET /points/:id} : get the "id" points. + * + * @param id the id of the points to retrieve. + * @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body the points, or with status {@code 404 (Not Found)}. + */ + @GetMapping("/points/{id}") + public ResponseEntity getPoints(@PathVariable Long id) { + log.debug("REST request to get Points : {}", id); + Optional points = pointsRepository.findOneWithEagerRelationships(id); + return ResponseUtil.wrapOrNotFound(points); + } + + /** + * {@code DELETE /points/:id} : delete the "id" points. + * + * @param id the id of the points to delete. + * @return the {@link ResponseEntity} with status {@code 204 (NO_CONTENT)}. + */ + @DeleteMapping("/points/{id}") + public ResponseEntity deletePoints(@PathVariable Long id) { + log.debug("REST request to delete Points : {}", id); + pointsRepository.deleteById(id); + pointsSearchRepository.deleteById(id); + return ResponseEntity + .noContent() + .headers(HeaderUtil.createEntityDeletionAlert(applicationName, true, ENTITY_NAME, id.toString())) + .build(); + } + + /** + * {@code SEARCH /_search/points?query=:query} : search for the points corresponding + * to the query. + * + * @param query the query of the points search. + * @param pageable the pagination information. + * @return the result of the search. + */ + @GetMapping("/_search/points") + public ResponseEntity> searchPoints( + @RequestParam String query, + @org.springdoc.api.annotations.ParameterObject Pageable pageable + ) { + log.debug("REST request to search for a page of Points for query {}", query); + Page page = pointsSearchRepository.search(query, pageable); + HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(ServletUriComponentsBuilder.fromCurrentRequest(), page); + return ResponseEntity.ok().headers(headers).body(page.getContent()); + } +} diff --git a/src/main/java/org/jhipster/health/web/rest/PreferencesResource.java b/src/main/java/org/jhipster/health/web/rest/PreferencesResource.java new file mode 100644 index 00000000..de84342e --- /dev/null +++ b/src/main/java/org/jhipster/health/web/rest/PreferencesResource.java @@ -0,0 +1,218 @@ +package org.jhipster.health.web.rest; + +import static org.elasticsearch.index.query.QueryBuilders.*; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import org.jhipster.health.domain.Preferences; +import org.jhipster.health.repository.PreferencesRepository; +import org.jhipster.health.repository.search.PreferencesSearchRepository; +import org.jhipster.health.web.rest.errors.BadRequestAlertException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.ResponseEntity; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.*; +import tech.jhipster.web.util.HeaderUtil; +import tech.jhipster.web.util.ResponseUtil; + +/** + * REST controller for managing {@link org.jhipster.health.domain.Preferences}. + */ +@RestController +@RequestMapping("/api") +@Transactional +public class PreferencesResource { + + private final Logger log = LoggerFactory.getLogger(PreferencesResource.class); + + private static final String ENTITY_NAME = "preferences"; + + @Value("${jhipster.clientApp.name}") + private String applicationName; + + private final PreferencesRepository preferencesRepository; + + private final PreferencesSearchRepository preferencesSearchRepository; + + public PreferencesResource(PreferencesRepository preferencesRepository, PreferencesSearchRepository preferencesSearchRepository) { + this.preferencesRepository = preferencesRepository; + this.preferencesSearchRepository = preferencesSearchRepository; + } + + /** + * {@code POST /preferences} : Create a new preferences. + * + * @param preferences the preferences to create. + * @return the {@link ResponseEntity} with status {@code 201 (Created)} and with body the new preferences, or with status {@code 400 (Bad Request)} if the preferences has already an ID. + * @throws URISyntaxException if the Location URI syntax is incorrect. + */ + @PostMapping("/preferences") + public ResponseEntity createPreferences(@Valid @RequestBody Preferences preferences) throws URISyntaxException { + log.debug("REST request to save Preferences : {}", preferences); + if (preferences.getId() != null) { + throw new BadRequestAlertException("A new preferences cannot already have an ID", ENTITY_NAME, "idexists"); + } + Preferences result = preferencesRepository.save(preferences); + preferencesSearchRepository.index(result); + return ResponseEntity + .created(new URI("/api/preferences/" + result.getId())) + .headers(HeaderUtil.createEntityCreationAlert(applicationName, true, ENTITY_NAME, result.getId().toString())) + .body(result); + } + + /** + * {@code PUT /preferences/:id} : Updates an existing preferences. + * + * @param id the id of the preferences to save. + * @param preferences the preferences to update. + * @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body the updated preferences, + * or with status {@code 400 (Bad Request)} if the preferences is not valid, + * or with status {@code 500 (Internal Server Error)} if the preferences couldn't be updated. + * @throws URISyntaxException if the Location URI syntax is incorrect. + */ + @PutMapping("/preferences/{id}") + public ResponseEntity updatePreferences( + @PathVariable(value = "id", required = false) final Long id, + @Valid @RequestBody Preferences preferences + ) throws URISyntaxException { + log.debug("REST request to update Preferences : {}, {}", id, preferences); + if (preferences.getId() == null) { + throw new BadRequestAlertException("Invalid id", ENTITY_NAME, "idnull"); + } + if (!Objects.equals(id, preferences.getId())) { + throw new BadRequestAlertException("Invalid ID", ENTITY_NAME, "idinvalid"); + } + + if (!preferencesRepository.existsById(id)) { + throw new BadRequestAlertException("Entity not found", ENTITY_NAME, "idnotfound"); + } + + Preferences result = preferencesRepository.save(preferences); + preferencesSearchRepository.index(result); + return ResponseEntity + .ok() + .headers(HeaderUtil.createEntityUpdateAlert(applicationName, true, ENTITY_NAME, preferences.getId().toString())) + .body(result); + } + + /** + * {@code PATCH /preferences/:id} : Partial updates given fields of an existing preferences, field will ignore if it is null + * + * @param id the id of the preferences to save. + * @param preferences the preferences to update. + * @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body the updated preferences, + * or with status {@code 400 (Bad Request)} if the preferences is not valid, + * or with status {@code 404 (Not Found)} if the preferences is not found, + * or with status {@code 500 (Internal Server Error)} if the preferences couldn't be updated. + * @throws URISyntaxException if the Location URI syntax is incorrect. + */ + @PatchMapping(value = "/preferences/{id}", consumes = { "application/json", "application/merge-patch+json" }) + public ResponseEntity partialUpdatePreferences( + @PathVariable(value = "id", required = false) final Long id, + @NotNull @RequestBody Preferences preferences + ) throws URISyntaxException { + log.debug("REST request to partial update Preferences partially : {}, {}", id, preferences); + if (preferences.getId() == null) { + throw new BadRequestAlertException("Invalid id", ENTITY_NAME, "idnull"); + } + if (!Objects.equals(id, preferences.getId())) { + throw new BadRequestAlertException("Invalid ID", ENTITY_NAME, "idinvalid"); + } + + if (!preferencesRepository.existsById(id)) { + throw new BadRequestAlertException("Entity not found", ENTITY_NAME, "idnotfound"); + } + + Optional result = preferencesRepository + .findById(preferences.getId()) + .map(existingPreferences -> { + if (preferences.getWeeklyGoal() != null) { + existingPreferences.setWeeklyGoal(preferences.getWeeklyGoal()); + } + if (preferences.getWeightUnits() != null) { + existingPreferences.setWeightUnits(preferences.getWeightUnits()); + } + + return existingPreferences; + }) + .map(preferencesRepository::save) + .map(savedPreferences -> { + preferencesSearchRepository.save(savedPreferences); + + return savedPreferences; + }); + + return ResponseUtil.wrapOrNotFound( + result, + HeaderUtil.createEntityUpdateAlert(applicationName, true, ENTITY_NAME, preferences.getId().toString()) + ); + } + + /** + * {@code GET /preferences} : get all the preferences. + * + * @param eagerload flag to eager load entities from relationships (This is applicable for many-to-many). + * @return the {@link ResponseEntity} with status {@code 200 (OK)} and the list of preferences in body. + */ + @GetMapping("/preferences") + public List getAllPreferences(@RequestParam(required = false, defaultValue = "false") boolean eagerload) { + log.debug("REST request to get all Preferences"); + if (eagerload) { + return preferencesRepository.findAllWithEagerRelationships(); + } else { + return preferencesRepository.findAll(); + } + } + + /** + * {@code GET /preferences/:id} : get the "id" preferences. + * + * @param id the id of the preferences to retrieve. + * @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body the preferences, or with status {@code 404 (Not Found)}. + */ + @GetMapping("/preferences/{id}") + public ResponseEntity getPreferences(@PathVariable Long id) { + log.debug("REST request to get Preferences : {}", id); + Optional preferences = preferencesRepository.findOneWithEagerRelationships(id); + return ResponseUtil.wrapOrNotFound(preferences); + } + + /** + * {@code DELETE /preferences/:id} : delete the "id" preferences. + * + * @param id the id of the preferences to delete. + * @return the {@link ResponseEntity} with status {@code 204 (NO_CONTENT)}. + */ + @DeleteMapping("/preferences/{id}") + public ResponseEntity deletePreferences(@PathVariable Long id) { + log.debug("REST request to delete Preferences : {}", id); + preferencesRepository.deleteById(id); + preferencesSearchRepository.deleteById(id); + return ResponseEntity + .noContent() + .headers(HeaderUtil.createEntityDeletionAlert(applicationName, true, ENTITY_NAME, id.toString())) + .build(); + } + + /** + * {@code SEARCH /_search/preferences?query=:query} : search for the preferences corresponding + * to the query. + * + * @param query the query of the preferences search. + * @return the result of the search. + */ + @GetMapping("/_search/preferences") + public List searchPreferences(@RequestParam String query) { + log.debug("REST request to search Preferences for query {}", query); + return StreamSupport.stream(preferencesSearchRepository.search(query).spliterator(), false).collect(Collectors.toList()); + } +} diff --git a/src/main/java/org/jhipster/health/web/rest/PublicUserResource.java b/src/main/java/org/jhipster/health/web/rest/PublicUserResource.java new file mode 100644 index 00000000..af661047 --- /dev/null +++ b/src/main/java/org/jhipster/health/web/rest/PublicUserResource.java @@ -0,0 +1,83 @@ +package org.jhipster.health.web.rest; + +import static org.elasticsearch.index.query.QueryBuilders.*; + +import java.util.*; +import java.util.Collections; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; +import org.jhipster.health.repository.search.UserSearchRepository; +import org.jhipster.health.service.UserService; +import org.jhipster.health.service.dto.UserDTO; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; +import tech.jhipster.web.util.PaginationUtil; + +@RestController +@RequestMapping("/api") +public class PublicUserResource { + + private static final List ALLOWED_ORDERED_PROPERTIES = Collections.unmodifiableList( + Arrays.asList("id", "login", "firstName", "lastName", "email", "activated", "langKey") + ); + + private final Logger log = LoggerFactory.getLogger(PublicUserResource.class); + + private final UserService userService; + private final UserSearchRepository userSearchRepository; + + public PublicUserResource(UserSearchRepository userSearchRepository, UserService userService) { + this.userService = userService; + this.userSearchRepository = userSearchRepository; + } + + /** + * {@code GET /users} : get all users with only the public informations - calling this are allowed for anyone. + * + * @param pageable the pagination information. + * @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body all users. + */ + @GetMapping("/users") + public ResponseEntity> getAllPublicUsers(@org.springdoc.api.annotations.ParameterObject Pageable pageable) { + log.debug("REST request to get all public User names"); + if (!onlyContainsAllowedProperties(pageable)) { + return ResponseEntity.badRequest().build(); + } + + final Page page = userService.getAllPublicUsers(pageable); + HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(ServletUriComponentsBuilder.fromCurrentRequest(), page); + return new ResponseEntity<>(page.getContent(), headers, HttpStatus.OK); + } + + private boolean onlyContainsAllowedProperties(Pageable pageable) { + return pageable.getSort().stream().map(Sort.Order::getProperty).allMatch(ALLOWED_ORDERED_PROPERTIES::contains); + } + + /** + * Gets a list of all roles. + * @return a string list of all roles. + */ + @GetMapping("/authorities") + public List getAuthorities() { + return userService.getAuthorities(); + } + + /** + * {@code SEARCH /_search/users/:query} : search for the User corresponding to the query. + * + * @param query the query to search. + * @return the result of the search. + */ + @GetMapping("/_search/users/{query}") + public List search(@PathVariable String query) { + return StreamSupport.stream(userSearchRepository.search(query).spliterator(), false).map(UserDTO::new).collect(Collectors.toList()); + } +} diff --git a/src/main/java/org/jhipster/health/web/rest/UserJWTController.java b/src/main/java/org/jhipster/health/web/rest/UserJWTController.java new file mode 100644 index 00000000..1c7e25f9 --- /dev/null +++ b/src/main/java/org/jhipster/health/web/rest/UserJWTController.java @@ -0,0 +1,68 @@ +package org.jhipster.health.web.rest; + +import com.fasterxml.jackson.annotation.JsonProperty; +import javax.validation.Valid; +import org.jhipster.health.security.jwt.JWTFilter; +import org.jhipster.health.security.jwt.TokenProvider; +import org.jhipster.health.web.rest.vm.LoginVM; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.web.bind.annotation.*; + +/** + * Controller to authenticate users. + */ +@RestController +@RequestMapping("/api") +public class UserJWTController { + + private final TokenProvider tokenProvider; + + private final AuthenticationManagerBuilder authenticationManagerBuilder; + + public UserJWTController(TokenProvider tokenProvider, AuthenticationManagerBuilder authenticationManagerBuilder) { + this.tokenProvider = tokenProvider; + this.authenticationManagerBuilder = authenticationManagerBuilder; + } + + @PostMapping("/authenticate") + public ResponseEntity authorize(@Valid @RequestBody LoginVM loginVM) { + UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken( + loginVM.getUsername(), + loginVM.getPassword() + ); + + Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken); + SecurityContextHolder.getContext().setAuthentication(authentication); + String jwt = tokenProvider.createToken(authentication, loginVM.isRememberMe()); + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.add(JWTFilter.AUTHORIZATION_HEADER, "Bearer " + jwt); + return new ResponseEntity<>(new JWTToken(jwt), httpHeaders, HttpStatus.OK); + } + + /** + * Object to return as body in JWT Authentication. + */ + static class JWTToken { + + private String idToken; + + JWTToken(String idToken) { + this.idToken = idToken; + } + + @JsonProperty("id_token") + String getIdToken() { + return idToken; + } + + void setIdToken(String idToken) { + this.idToken = idToken; + } + } +} diff --git a/src/main/java/org/jhipster/health/web/rest/UserResource.java b/src/main/java/org/jhipster/health/web/rest/UserResource.java new file mode 100644 index 00000000..e61652e1 --- /dev/null +++ b/src/main/java/org/jhipster/health/web/rest/UserResource.java @@ -0,0 +1,211 @@ +package org.jhipster.health.web.rest; + +import static org.elasticsearch.index.query.QueryBuilders.*; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.*; +import java.util.Collections; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; +import javax.validation.Valid; +import javax.validation.constraints.Pattern; +import org.jhipster.health.config.Constants; +import org.jhipster.health.domain.User; +import org.jhipster.health.repository.UserRepository; +import org.jhipster.health.security.AuthoritiesConstants; +import org.jhipster.health.service.MailService; +import org.jhipster.health.service.UserService; +import org.jhipster.health.service.dto.AdminUserDTO; +import org.jhipster.health.web.rest.errors.BadRequestAlertException; +import org.jhipster.health.web.rest.errors.EmailAlreadyUsedException; +import org.jhipster.health.web.rest.errors.LoginAlreadyUsedException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; +import tech.jhipster.web.util.HeaderUtil; +import tech.jhipster.web.util.PaginationUtil; +import tech.jhipster.web.util.ResponseUtil; + +/** + * REST controller for managing users. + *

+ * This class accesses the {@link org.jhipster.health.domain.User} entity, and needs to fetch its collection of authorities. + *

+ * For a normal use-case, it would be better to have an eager relationship between User and Authority, + * and send everything to the client side: there would be no View Model and DTO, a lot less code, and an outer-join + * which would be good for performance. + *

+ * We use a View Model and a DTO for 3 reasons: + *

    + *
  • We want to keep a lazy association between the user and the authorities, because people will + * quite often do relationships with the user, and we don't want them to get the authorities all + * the time for nothing (for performance reasons). This is the #1 goal: we should not impact our users' + * application because of this use-case.
  • + *
  • Not having an outer join causes n+1 requests to the database. This is not a real issue as + * we have by default a second-level cache. This means on the first HTTP call we do the n+1 requests, + * but then all authorities come from the cache, so in fact it's much better than doing an outer join + * (which will get lots of data from the database, for each HTTP call).
  • + *
  • As this manages users, for security reasons, we'd rather have a DTO layer.
  • + *
+ *

+ * Another option would be to have a specific JPA entity graph to handle this case. + */ +@RestController +@RequestMapping("/api/admin") +public class UserResource { + + private static final List ALLOWED_ORDERED_PROPERTIES = Collections.unmodifiableList( + Arrays.asList( + "id", + "login", + "firstName", + "lastName", + "email", + "activated", + "langKey", + "createdBy", + "createdDate", + "lastModifiedBy", + "lastModifiedDate" + ) + ); + + private final Logger log = LoggerFactory.getLogger(UserResource.class); + + @Value("${jhipster.clientApp.name}") + private String applicationName; + + private final UserService userService; + + private final UserRepository userRepository; + + private final MailService mailService; + + public UserResource(UserService userService, UserRepository userRepository, MailService mailService) { + this.userService = userService; + this.userRepository = userRepository; + this.mailService = mailService; + } + + /** + * {@code POST /admin/users} : Creates a new user. + *

+ * Creates a new user if the login and email are not already used, and sends an + * mail with an activation link. + * The user needs to be activated on creation. + * + * @param userDTO the user to create. + * @return the {@link ResponseEntity} with status {@code 201 (Created)} and with body the new user, or with status {@code 400 (Bad Request)} if the login or email is already in use. + * @throws URISyntaxException if the Location URI syntax is incorrect. + * @throws BadRequestAlertException {@code 400 (Bad Request)} if the login or email is already in use. + */ + @PostMapping("/users") + @PreAuthorize("hasAuthority(\"" + AuthoritiesConstants.ADMIN + "\")") + public ResponseEntity createUser(@Valid @RequestBody AdminUserDTO userDTO) throws URISyntaxException { + log.debug("REST request to save User : {}", userDTO); + + if (userDTO.getId() != null) { + throw new BadRequestAlertException("A new user cannot already have an ID", "userManagement", "idexists"); + // Lowercase the user login before comparing with database + } else if (userRepository.findOneByLogin(userDTO.getLogin().toLowerCase()).isPresent()) { + throw new LoginAlreadyUsedException(); + } else if (userRepository.findOneByEmailIgnoreCase(userDTO.getEmail()).isPresent()) { + throw new EmailAlreadyUsedException(); + } else { + User newUser = userService.createUser(userDTO); + mailService.sendCreationEmail(newUser); + return ResponseEntity + .created(new URI("/api/admin/users/" + newUser.getLogin())) + .headers(HeaderUtil.createAlert(applicationName, "userManagement.created", newUser.getLogin())) + .body(newUser); + } + } + + /** + * {@code PUT /admin/users} : Updates an existing User. + * + * @param userDTO the user to update. + * @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body the updated user. + * @throws EmailAlreadyUsedException {@code 400 (Bad Request)} if the email is already in use. + * @throws LoginAlreadyUsedException {@code 400 (Bad Request)} if the login is already in use. + */ + @PutMapping("/users") + @PreAuthorize("hasAuthority(\"" + AuthoritiesConstants.ADMIN + "\")") + public ResponseEntity updateUser(@Valid @RequestBody AdminUserDTO userDTO) { + log.debug("REST request to update User : {}", userDTO); + Optional existingUser = userRepository.findOneByEmailIgnoreCase(userDTO.getEmail()); + if (existingUser.isPresent() && (!existingUser.get().getId().equals(userDTO.getId()))) { + throw new EmailAlreadyUsedException(); + } + existingUser = userRepository.findOneByLogin(userDTO.getLogin().toLowerCase()); + if (existingUser.isPresent() && (!existingUser.get().getId().equals(userDTO.getId()))) { + throw new LoginAlreadyUsedException(); + } + Optional updatedUser = userService.updateUser(userDTO); + + return ResponseUtil.wrapOrNotFound( + updatedUser, + HeaderUtil.createAlert(applicationName, "userManagement.updated", userDTO.getLogin()) + ); + } + + /** + * {@code GET /admin/users} : get all users with all the details - calling this are only allowed for the administrators. + * + * @param pageable the pagination information. + * @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body all users. + */ + @GetMapping("/users") + @PreAuthorize("hasAuthority(\"" + AuthoritiesConstants.ADMIN + "\")") + public ResponseEntity> getAllUsers(@org.springdoc.api.annotations.ParameterObject Pageable pageable) { + log.debug("REST request to get all User for an admin"); + if (!onlyContainsAllowedProperties(pageable)) { + return ResponseEntity.badRequest().build(); + } + + final Page page = userService.getAllManagedUsers(pageable); + HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(ServletUriComponentsBuilder.fromCurrentRequest(), page); + return new ResponseEntity<>(page.getContent(), headers, HttpStatus.OK); + } + + private boolean onlyContainsAllowedProperties(Pageable pageable) { + return pageable.getSort().stream().map(Sort.Order::getProperty).allMatch(ALLOWED_ORDERED_PROPERTIES::contains); + } + + /** + * {@code GET /admin/users/:login} : get the "login" user. + * + * @param login the login of the user to find. + * @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body the "login" user, or with status {@code 404 (Not Found)}. + */ + @GetMapping("/users/{login}") + @PreAuthorize("hasAuthority(\"" + AuthoritiesConstants.ADMIN + "\")") + public ResponseEntity getUser(@PathVariable @Pattern(regexp = Constants.LOGIN_REGEX) String login) { + log.debug("REST request to get User : {}", login); + return ResponseUtil.wrapOrNotFound(userService.getUserWithAuthoritiesByLogin(login).map(AdminUserDTO::new)); + } + + /** + * {@code DELETE /admin/users/:login} : delete the "login" User. + * + * @param login the login of the user to delete. + * @return the {@link ResponseEntity} with status {@code 204 (NO_CONTENT)}. + */ + @DeleteMapping("/users/{login}") + @PreAuthorize("hasAuthority(\"" + AuthoritiesConstants.ADMIN + "\")") + public ResponseEntity deleteUser(@PathVariable @Pattern(regexp = Constants.LOGIN_REGEX) String login) { + log.debug("REST request to delete User: {}", login); + userService.deleteUser(login); + return ResponseEntity.noContent().headers(HeaderUtil.createAlert(applicationName, "userManagement.deleted", login)).build(); + } +} diff --git a/src/main/java/org/jhipster/health/web/rest/WeightResource.java b/src/main/java/org/jhipster/health/web/rest/WeightResource.java new file mode 100644 index 00000000..7ebef33a --- /dev/null +++ b/src/main/java/org/jhipster/health/web/rest/WeightResource.java @@ -0,0 +1,237 @@ +package org.jhipster.health.web.rest; + +import static org.elasticsearch.index.query.QueryBuilders.*; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import org.jhipster.health.domain.Weight; +import org.jhipster.health.repository.WeightRepository; +import org.jhipster.health.repository.search.WeightSearchRepository; +import org.jhipster.health.web.rest.errors.BadRequestAlertException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.servlet.support.ServletUriComponentsBuilder; +import tech.jhipster.web.util.HeaderUtil; +import tech.jhipster.web.util.PaginationUtil; +import tech.jhipster.web.util.ResponseUtil; + +/** + * REST controller for managing {@link org.jhipster.health.domain.Weight}. + */ +@RestController +@RequestMapping("/api") +@Transactional +public class WeightResource { + + private final Logger log = LoggerFactory.getLogger(WeightResource.class); + + private static final String ENTITY_NAME = "weight"; + + @Value("${jhipster.clientApp.name}") + private String applicationName; + + private final WeightRepository weightRepository; + + private final WeightSearchRepository weightSearchRepository; + + public WeightResource(WeightRepository weightRepository, WeightSearchRepository weightSearchRepository) { + this.weightRepository = weightRepository; + this.weightSearchRepository = weightSearchRepository; + } + + /** + * {@code POST /weights} : Create a new weight. + * + * @param weight the weight to create. + * @return the {@link ResponseEntity} with status {@code 201 (Created)} and with body the new weight, or with status {@code 400 (Bad Request)} if the weight has already an ID. + * @throws URISyntaxException if the Location URI syntax is incorrect. + */ + @PostMapping("/weights") + public ResponseEntity createWeight(@Valid @RequestBody Weight weight) throws URISyntaxException { + log.debug("REST request to save Weight : {}", weight); + if (weight.getId() != null) { + throw new BadRequestAlertException("A new weight cannot already have an ID", ENTITY_NAME, "idexists"); + } + Weight result = weightRepository.save(weight); + weightSearchRepository.index(result); + return ResponseEntity + .created(new URI("/api/weights/" + result.getId())) + .headers(HeaderUtil.createEntityCreationAlert(applicationName, true, ENTITY_NAME, result.getId().toString())) + .body(result); + } + + /** + * {@code PUT /weights/:id} : Updates an existing weight. + * + * @param id the id of the weight to save. + * @param weight the weight to update. + * @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body the updated weight, + * or with status {@code 400 (Bad Request)} if the weight is not valid, + * or with status {@code 500 (Internal Server Error)} if the weight couldn't be updated. + * @throws URISyntaxException if the Location URI syntax is incorrect. + */ + @PutMapping("/weights/{id}") + public ResponseEntity updateWeight( + @PathVariable(value = "id", required = false) final Long id, + @Valid @RequestBody Weight weight + ) throws URISyntaxException { + log.debug("REST request to update Weight : {}, {}", id, weight); + if (weight.getId() == null) { + throw new BadRequestAlertException("Invalid id", ENTITY_NAME, "idnull"); + } + if (!Objects.equals(id, weight.getId())) { + throw new BadRequestAlertException("Invalid ID", ENTITY_NAME, "idinvalid"); + } + + if (!weightRepository.existsById(id)) { + throw new BadRequestAlertException("Entity not found", ENTITY_NAME, "idnotfound"); + } + + Weight result = weightRepository.save(weight); + weightSearchRepository.index(result); + return ResponseEntity + .ok() + .headers(HeaderUtil.createEntityUpdateAlert(applicationName, true, ENTITY_NAME, weight.getId().toString())) + .body(result); + } + + /** + * {@code PATCH /weights/:id} : Partial updates given fields of an existing weight, field will ignore if it is null + * + * @param id the id of the weight to save. + * @param weight the weight to update. + * @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body the updated weight, + * or with status {@code 400 (Bad Request)} if the weight is not valid, + * or with status {@code 404 (Not Found)} if the weight is not found, + * or with status {@code 500 (Internal Server Error)} if the weight couldn't be updated. + * @throws URISyntaxException if the Location URI syntax is incorrect. + */ + @PatchMapping(value = "/weights/{id}", consumes = { "application/json", "application/merge-patch+json" }) + public ResponseEntity partialUpdateWeight( + @PathVariable(value = "id", required = false) final Long id, + @NotNull @RequestBody Weight weight + ) throws URISyntaxException { + log.debug("REST request to partial update Weight partially : {}, {}", id, weight); + if (weight.getId() == null) { + throw new BadRequestAlertException("Invalid id", ENTITY_NAME, "idnull"); + } + if (!Objects.equals(id, weight.getId())) { + throw new BadRequestAlertException("Invalid ID", ENTITY_NAME, "idinvalid"); + } + + if (!weightRepository.existsById(id)) { + throw new BadRequestAlertException("Entity not found", ENTITY_NAME, "idnotfound"); + } + + Optional result = weightRepository + .findById(weight.getId()) + .map(existingWeight -> { + if (weight.getTimestamp() != null) { + existingWeight.setTimestamp(weight.getTimestamp()); + } + if (weight.getWeight() != null) { + existingWeight.setWeight(weight.getWeight()); + } + + return existingWeight; + }) + .map(weightRepository::save) + .map(savedWeight -> { + weightSearchRepository.save(savedWeight); + + return savedWeight; + }); + + return ResponseUtil.wrapOrNotFound( + result, + HeaderUtil.createEntityUpdateAlert(applicationName, true, ENTITY_NAME, weight.getId().toString()) + ); + } + + /** + * {@code GET /weights} : get all the weights. + * + * @param pageable the pagination information. + * @param eagerload flag to eager load entities from relationships (This is applicable for many-to-many). + * @return the {@link ResponseEntity} with status {@code 200 (OK)} and the list of weights in body. + */ + @GetMapping("/weights") + public ResponseEntity> getAllWeights( + @org.springdoc.api.annotations.ParameterObject Pageable pageable, + @RequestParam(required = false, defaultValue = "false") boolean eagerload + ) { + log.debug("REST request to get a page of Weights"); + Page page; + if (eagerload) { + page = weightRepository.findAllWithEagerRelationships(pageable); + } else { + page = weightRepository.findAll(pageable); + } + HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(ServletUriComponentsBuilder.fromCurrentRequest(), page); + return ResponseEntity.ok().headers(headers).body(page.getContent()); + } + + /** + * {@code GET /weights/:id} : get the "id" weight. + * + * @param id the id of the weight to retrieve. + * @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body the weight, or with status {@code 404 (Not Found)}. + */ + @GetMapping("/weights/{id}") + public ResponseEntity getWeight(@PathVariable Long id) { + log.debug("REST request to get Weight : {}", id); + Optional weight = weightRepository.findOneWithEagerRelationships(id); + return ResponseUtil.wrapOrNotFound(weight); + } + + /** + * {@code DELETE /weights/:id} : delete the "id" weight. + * + * @param id the id of the weight to delete. + * @return the {@link ResponseEntity} with status {@code 204 (NO_CONTENT)}. + */ + @DeleteMapping("/weights/{id}") + public ResponseEntity deleteWeight(@PathVariable Long id) { + log.debug("REST request to delete Weight : {}", id); + weightRepository.deleteById(id); + weightSearchRepository.deleteById(id); + return ResponseEntity + .noContent() + .headers(HeaderUtil.createEntityDeletionAlert(applicationName, true, ENTITY_NAME, id.toString())) + .build(); + } + + /** + * {@code SEARCH /_search/weights?query=:query} : search for the weight corresponding + * to the query. + * + * @param query the query of the weight search. + * @param pageable the pagination information. + * @return the result of the search. + */ + @GetMapping("/_search/weights") + public ResponseEntity> searchWeights( + @RequestParam String query, + @org.springdoc.api.annotations.ParameterObject Pageable pageable + ) { + log.debug("REST request to search for a page of Weights for query {}", query); + Page page = weightSearchRepository.search(query, pageable); + HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(ServletUriComponentsBuilder.fromCurrentRequest(), page); + return ResponseEntity.ok().headers(headers).body(page.getContent()); + } +} diff --git a/src/main/java/org/jhipster/health/web/rest/errors/BadRequestAlertException.java b/src/main/java/org/jhipster/health/web/rest/errors/BadRequestAlertException.java new file mode 100644 index 00000000..4f5018e1 --- /dev/null +++ b/src/main/java/org/jhipster/health/web/rest/errors/BadRequestAlertException.java @@ -0,0 +1,42 @@ +package org.jhipster.health.web.rest.errors; + +import java.net.URI; +import java.util.HashMap; +import java.util.Map; +import org.zalando.problem.AbstractThrowableProblem; +import org.zalando.problem.Status; + +@SuppressWarnings("java:S110") // Inheritance tree of classes should not be too deep +public class BadRequestAlertException extends AbstractThrowableProblem { + + private static final long serialVersionUID = 1L; + + private final String entityName; + + private final String errorKey; + + public BadRequestAlertException(String defaultMessage, String entityName, String errorKey) { + this(ErrorConstants.DEFAULT_TYPE, defaultMessage, entityName, errorKey); + } + + public BadRequestAlertException(URI type, String defaultMessage, String entityName, String errorKey) { + super(type, defaultMessage, Status.BAD_REQUEST, null, null, null, getAlertParameters(entityName, errorKey)); + this.entityName = entityName; + this.errorKey = errorKey; + } + + public String getEntityName() { + return entityName; + } + + public String getErrorKey() { + return errorKey; + } + + private static Map getAlertParameters(String entityName, String errorKey) { + Map parameters = new HashMap<>(); + parameters.put("message", "error." + errorKey); + parameters.put("params", entityName); + return parameters; + } +} diff --git a/src/main/java/org/jhipster/health/web/rest/errors/EmailAlreadyUsedException.java b/src/main/java/org/jhipster/health/web/rest/errors/EmailAlreadyUsedException.java new file mode 100644 index 00000000..b0d0127c --- /dev/null +++ b/src/main/java/org/jhipster/health/web/rest/errors/EmailAlreadyUsedException.java @@ -0,0 +1,11 @@ +package org.jhipster.health.web.rest.errors; + +@SuppressWarnings("java:S110") // Inheritance tree of classes should not be too deep +public class EmailAlreadyUsedException extends BadRequestAlertException { + + private static final long serialVersionUID = 1L; + + public EmailAlreadyUsedException() { + super(ErrorConstants.EMAIL_ALREADY_USED_TYPE, "Email is already in use!", "userManagement", "emailexists"); + } +} diff --git a/src/main/java/org/jhipster/health/web/rest/errors/ErrorConstants.java b/src/main/java/org/jhipster/health/web/rest/errors/ErrorConstants.java new file mode 100644 index 00000000..c17eb7f7 --- /dev/null +++ b/src/main/java/org/jhipster/health/web/rest/errors/ErrorConstants.java @@ -0,0 +1,17 @@ +package org.jhipster.health.web.rest.errors; + +import java.net.URI; + +public final class ErrorConstants { + + public static final String ERR_CONCURRENCY_FAILURE = "error.concurrencyFailure"; + public static final String ERR_VALIDATION = "error.validation"; + public static final String PROBLEM_BASE_URL = "https://www.jhipster.tech/problem"; + public static final URI DEFAULT_TYPE = URI.create(PROBLEM_BASE_URL + "/problem-with-message"); + public static final URI CONSTRAINT_VIOLATION_TYPE = URI.create(PROBLEM_BASE_URL + "/constraint-violation"); + public static final URI INVALID_PASSWORD_TYPE = URI.create(PROBLEM_BASE_URL + "/invalid-password"); + public static final URI EMAIL_ALREADY_USED_TYPE = URI.create(PROBLEM_BASE_URL + "/email-already-used"); + public static final URI LOGIN_ALREADY_USED_TYPE = URI.create(PROBLEM_BASE_URL + "/login-already-used"); + + private ErrorConstants() {} +} diff --git a/src/main/java/org/jhipster/health/web/rest/errors/ExceptionTranslator.java b/src/main/java/org/jhipster/health/web/rest/errors/ExceptionTranslator.java new file mode 100644 index 00000000..e5bf1085 --- /dev/null +++ b/src/main/java/org/jhipster/health/web/rest/errors/ExceptionTranslator.java @@ -0,0 +1,222 @@ +package org.jhipster.health.web.rest.errors; + +import java.net.URI; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.servlet.http.HttpServletRequest; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.env.Environment; +import org.springframework.dao.ConcurrencyFailureException; +import org.springframework.dao.DataAccessException; +import org.springframework.http.ResponseEntity; +import org.springframework.http.converter.HttpMessageConversionException; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ControllerAdvice; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.context.request.NativeWebRequest; +import org.zalando.problem.DefaultProblem; +import org.zalando.problem.Problem; +import org.zalando.problem.ProblemBuilder; +import org.zalando.problem.Status; +import org.zalando.problem.StatusType; +import org.zalando.problem.spring.web.advice.ProblemHandling; +import org.zalando.problem.spring.web.advice.security.SecurityAdviceTrait; +import org.zalando.problem.violations.ConstraintViolationProblem; +import tech.jhipster.config.JHipsterConstants; +import tech.jhipster.web.util.HeaderUtil; + +/** + * Controller advice to translate the server side exceptions to client-friendly json structures. + * The error response follows RFC7807 - Problem Details for HTTP APIs (https://tools.ietf.org/html/rfc7807). + */ +@ControllerAdvice +public class ExceptionTranslator implements ProblemHandling, SecurityAdviceTrait { + + private static final String FIELD_ERRORS_KEY = "fieldErrors"; + private static final String MESSAGE_KEY = "message"; + private static final String PATH_KEY = "path"; + private static final String VIOLATIONS_KEY = "violations"; + + @Value("${jhipster.clientApp.name}") + private String applicationName; + + private final Environment env; + + public ExceptionTranslator(Environment env) { + this.env = env; + } + + /** + * Post-process the Problem payload to add the message key for the front-end if needed. + */ + @Override + public ResponseEntity process(@Nullable ResponseEntity entity, NativeWebRequest request) { + if (entity == null) { + return null; + } + Problem problem = entity.getBody(); + if (!(problem instanceof ConstraintViolationProblem || problem instanceof DefaultProblem)) { + return entity; + } + + HttpServletRequest nativeRequest = request.getNativeRequest(HttpServletRequest.class); + String requestUri = nativeRequest != null ? nativeRequest.getRequestURI() : StringUtils.EMPTY; + ProblemBuilder builder = Problem + .builder() + .withType(Problem.DEFAULT_TYPE.equals(problem.getType()) ? ErrorConstants.DEFAULT_TYPE : problem.getType()) + .withStatus(problem.getStatus()) + .withTitle(problem.getTitle()) + .with(PATH_KEY, requestUri); + + if (problem instanceof ConstraintViolationProblem) { + builder + .with(VIOLATIONS_KEY, ((ConstraintViolationProblem) problem).getViolations()) + .with(MESSAGE_KEY, ErrorConstants.ERR_VALIDATION); + } else { + builder.withCause(((DefaultProblem) problem).getCause()).withDetail(problem.getDetail()).withInstance(problem.getInstance()); + problem.getParameters().forEach(builder::with); + if (!problem.getParameters().containsKey(MESSAGE_KEY) && problem.getStatus() != null) { + builder.with(MESSAGE_KEY, "error.http." + problem.getStatus().getStatusCode()); + } + } + return new ResponseEntity<>(builder.build(), entity.getHeaders(), entity.getStatusCode()); + } + + @Override + public ResponseEntity handleMethodArgumentNotValid(MethodArgumentNotValidException ex, @Nonnull NativeWebRequest request) { + BindingResult result = ex.getBindingResult(); + List fieldErrors = result + .getFieldErrors() + .stream() + .map(f -> + new FieldErrorVM( + f.getObjectName().replaceFirst("DTO$", ""), + f.getField(), + StringUtils.isNotBlank(f.getDefaultMessage()) ? f.getDefaultMessage() : f.getCode() + ) + ) + .collect(Collectors.toList()); + + Problem problem = Problem + .builder() + .withType(ErrorConstants.CONSTRAINT_VIOLATION_TYPE) + .withTitle("Method argument not valid") + .withStatus(defaultConstraintViolationStatus()) + .with(MESSAGE_KEY, ErrorConstants.ERR_VALIDATION) + .with(FIELD_ERRORS_KEY, fieldErrors) + .build(); + return create(ex, problem, request); + } + + @ExceptionHandler + public ResponseEntity handleEmailAlreadyUsedException( + org.jhipster.health.service.EmailAlreadyUsedException ex, + NativeWebRequest request + ) { + EmailAlreadyUsedException problem = new EmailAlreadyUsedException(); + return create( + problem, + request, + HeaderUtil.createFailureAlert(applicationName, true, problem.getEntityName(), problem.getErrorKey(), problem.getMessage()) + ); + } + + @ExceptionHandler + public ResponseEntity handleUsernameAlreadyUsedException( + org.jhipster.health.service.UsernameAlreadyUsedException ex, + NativeWebRequest request + ) { + LoginAlreadyUsedException problem = new LoginAlreadyUsedException(); + return create( + problem, + request, + HeaderUtil.createFailureAlert(applicationName, true, problem.getEntityName(), problem.getErrorKey(), problem.getMessage()) + ); + } + + @ExceptionHandler + public ResponseEntity handleInvalidPasswordException( + org.jhipster.health.service.InvalidPasswordException ex, + NativeWebRequest request + ) { + return create(new InvalidPasswordException(), request); + } + + @ExceptionHandler + public ResponseEntity handleBadRequestAlertException(BadRequestAlertException ex, NativeWebRequest request) { + return create( + ex, + request, + HeaderUtil.createFailureAlert(applicationName, true, ex.getEntityName(), ex.getErrorKey(), ex.getMessage()) + ); + } + + @ExceptionHandler + public ResponseEntity handleConcurrencyFailure(ConcurrencyFailureException ex, NativeWebRequest request) { + Problem problem = Problem.builder().withStatus(Status.CONFLICT).with(MESSAGE_KEY, ErrorConstants.ERR_CONCURRENCY_FAILURE).build(); + return create(ex, problem, request); + } + + @Override + public ProblemBuilder prepare(final Throwable throwable, final StatusType status, final URI type) { + Collection activeProfiles = Arrays.asList(env.getActiveProfiles()); + + if (activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_PRODUCTION)) { + if (throwable instanceof HttpMessageConversionException) { + return Problem + .builder() + .withType(type) + .withTitle(status.getReasonPhrase()) + .withStatus(status) + .withDetail("Unable to convert http message") + .withCause( + Optional.ofNullable(throwable.getCause()).filter(cause -> isCausalChainsEnabled()).map(this::toProblem).orElse(null) + ); + } + if (throwable instanceof DataAccessException) { + return Problem + .builder() + .withType(type) + .withTitle(status.getReasonPhrase()) + .withStatus(status) + .withDetail("Failure during data access") + .withCause( + Optional.ofNullable(throwable.getCause()).filter(cause -> isCausalChainsEnabled()).map(this::toProblem).orElse(null) + ); + } + if (containsPackageName(throwable.getMessage())) { + return Problem + .builder() + .withType(type) + .withTitle(status.getReasonPhrase()) + .withStatus(status) + .withDetail("Unexpected runtime exception") + .withCause( + Optional.ofNullable(throwable.getCause()).filter(cause -> isCausalChainsEnabled()).map(this::toProblem).orElse(null) + ); + } + } + + return Problem + .builder() + .withType(type) + .withTitle(status.getReasonPhrase()) + .withStatus(status) + .withDetail(throwable.getMessage()) + .withCause( + Optional.ofNullable(throwable.getCause()).filter(cause -> isCausalChainsEnabled()).map(this::toProblem).orElse(null) + ); + } + + private boolean containsPackageName(String message) { + // This list is for sure not complete + return StringUtils.containsAny(message, "org.", "java.", "net.", "javax.", "com.", "io.", "de.", "org.jhipster.health"); + } +} diff --git a/src/main/java/org/jhipster/health/web/rest/errors/FieldErrorVM.java b/src/main/java/org/jhipster/health/web/rest/errors/FieldErrorVM.java new file mode 100644 index 00000000..64a5d38d --- /dev/null +++ b/src/main/java/org/jhipster/health/web/rest/errors/FieldErrorVM.java @@ -0,0 +1,32 @@ +package org.jhipster.health.web.rest.errors; + +import java.io.Serializable; + +public class FieldErrorVM implements Serializable { + + private static final long serialVersionUID = 1L; + + private final String objectName; + + private final String field; + + private final String message; + + public FieldErrorVM(String dto, String field, String message) { + this.objectName = dto; + this.field = field; + this.message = message; + } + + public String getObjectName() { + return objectName; + } + + public String getField() { + return field; + } + + public String getMessage() { + return message; + } +} diff --git a/src/main/java/org/jhipster/health/web/rest/errors/InvalidPasswordException.java b/src/main/java/org/jhipster/health/web/rest/errors/InvalidPasswordException.java new file mode 100644 index 00000000..cdad1c4d --- /dev/null +++ b/src/main/java/org/jhipster/health/web/rest/errors/InvalidPasswordException.java @@ -0,0 +1,14 @@ +package org.jhipster.health.web.rest.errors; + +import org.zalando.problem.AbstractThrowableProblem; +import org.zalando.problem.Status; + +@SuppressWarnings("java:S110") // Inheritance tree of classes should not be too deep +public class InvalidPasswordException extends AbstractThrowableProblem { + + private static final long serialVersionUID = 1L; + + public InvalidPasswordException() { + super(ErrorConstants.INVALID_PASSWORD_TYPE, "Incorrect password", Status.BAD_REQUEST); + } +} diff --git a/src/main/java/org/jhipster/health/web/rest/errors/LoginAlreadyUsedException.java b/src/main/java/org/jhipster/health/web/rest/errors/LoginAlreadyUsedException.java new file mode 100644 index 00000000..c7c54bea --- /dev/null +++ b/src/main/java/org/jhipster/health/web/rest/errors/LoginAlreadyUsedException.java @@ -0,0 +1,11 @@ +package org.jhipster.health.web.rest.errors; + +@SuppressWarnings("java:S110") // Inheritance tree of classes should not be too deep +public class LoginAlreadyUsedException extends BadRequestAlertException { + + private static final long serialVersionUID = 1L; + + public LoginAlreadyUsedException() { + super(ErrorConstants.LOGIN_ALREADY_USED_TYPE, "Login name already used!", "userManagement", "userexists"); + } +} diff --git a/src/main/java/org/jhipster/health/web/rest/errors/package-info.java b/src/main/java/org/jhipster/health/web/rest/errors/package-info.java new file mode 100644 index 00000000..3624a508 --- /dev/null +++ b/src/main/java/org/jhipster/health/web/rest/errors/package-info.java @@ -0,0 +1,6 @@ +/** + * Specific errors used with Zalando's "problem-spring-web" library. + * + * More information on https://github.com/zalando/problem-spring-web + */ +package org.jhipster.health.web.rest.errors; diff --git a/src/main/java/org/jhipster/health/web/rest/package-info.java b/src/main/java/org/jhipster/health/web/rest/package-info.java new file mode 100644 index 00000000..7287b937 --- /dev/null +++ b/src/main/java/org/jhipster/health/web/rest/package-info.java @@ -0,0 +1,4 @@ +/** + * Spring MVC REST controllers. + */ +package org.jhipster.health.web.rest; diff --git a/src/main/java/org/jhipster/health/web/rest/vm/KeyAndPasswordVM.java b/src/main/java/org/jhipster/health/web/rest/vm/KeyAndPasswordVM.java new file mode 100644 index 00000000..ab6196ce --- /dev/null +++ b/src/main/java/org/jhipster/health/web/rest/vm/KeyAndPasswordVM.java @@ -0,0 +1,27 @@ +package org.jhipster.health.web.rest.vm; + +/** + * View Model object for storing the user's key and password. + */ +public class KeyAndPasswordVM { + + private String key; + + private String newPassword; + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getNewPassword() { + return newPassword; + } + + public void setNewPassword(String newPassword) { + this.newPassword = newPassword; + } +} diff --git a/src/main/java/org/jhipster/health/web/rest/vm/LoginVM.java b/src/main/java/org/jhipster/health/web/rest/vm/LoginVM.java new file mode 100644 index 00000000..a853c39a --- /dev/null +++ b/src/main/java/org/jhipster/health/web/rest/vm/LoginVM.java @@ -0,0 +1,53 @@ +package org.jhipster.health.web.rest.vm; + +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; + +/** + * View Model object for storing a user's credentials. + */ +public class LoginVM { + + @NotNull + @Size(min = 1, max = 50) + private String username; + + @NotNull + @Size(min = 4, max = 100) + private String password; + + private boolean rememberMe; + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public boolean isRememberMe() { + return rememberMe; + } + + public void setRememberMe(boolean rememberMe) { + this.rememberMe = rememberMe; + } + + // prettier-ignore + @Override + public String toString() { + return "LoginVM{" + + "username='" + username + '\'' + + ", rememberMe=" + rememberMe + + '}'; + } +} diff --git a/src/main/java/org/jhipster/health/web/rest/vm/ManagedUserVM.java b/src/main/java/org/jhipster/health/web/rest/vm/ManagedUserVM.java new file mode 100644 index 00000000..d3e058d5 --- /dev/null +++ b/src/main/java/org/jhipster/health/web/rest/vm/ManagedUserVM.java @@ -0,0 +1,35 @@ +package org.jhipster.health.web.rest.vm; + +import javax.validation.constraints.Size; +import org.jhipster.health.service.dto.AdminUserDTO; + +/** + * View Model extending the AdminUserDTO, which is meant to be used in the user management UI. + */ +public class ManagedUserVM extends AdminUserDTO { + + public static final int PASSWORD_MIN_LENGTH = 4; + + public static final int PASSWORD_MAX_LENGTH = 100; + + @Size(min = PASSWORD_MIN_LENGTH, max = PASSWORD_MAX_LENGTH) + private String password; + + public ManagedUserVM() { + // Empty constructor needed for Jackson. + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + // prettier-ignore + @Override + public String toString() { + return "ManagedUserVM{" + super.toString() + "} "; + } +} diff --git a/src/main/java/org/jhipster/health/web/rest/vm/package-info.java b/src/main/java/org/jhipster/health/web/rest/vm/package-info.java new file mode 100644 index 00000000..874b4f69 --- /dev/null +++ b/src/main/java/org/jhipster/health/web/rest/vm/package-info.java @@ -0,0 +1,4 @@ +/** + * View Models used by Spring MVC REST controllers. + */ +package org.jhipster.health.web.rest.vm; diff --git a/src/main/resources/.h2.server.properties b/src/main/resources/.h2.server.properties new file mode 100644 index 00000000..9acd5c99 --- /dev/null +++ b/src/main/resources/.h2.server.properties @@ -0,0 +1,5 @@ +#H2 Server Properties +0=JHipster H2 (Disk)|org.h2.Driver|jdbc\:h2\:file\:./build/h2db/db/twentyonepoints|TwentyOnePoints +webAllowOthers=true +webPort=8092 +webSSL=false diff --git a/src/main/resources/banner.txt b/src/main/resources/banner.txt new file mode 100644 index 00000000..e0bc55aa --- /dev/null +++ b/src/main/resources/banner.txt @@ -0,0 +1,10 @@ + + ${AnsiColor.GREEN} ██╗${AnsiColor.RED} ██╗ ██╗ ████████╗ ███████╗ ██████╗ ████████╗ ████████╗ ███████╗ + ${AnsiColor.GREEN} ██║${AnsiColor.RED} ██║ ██║ ╚══██╔══╝ ██╔═══██╗ ██╔════╝ ╚══██╔══╝ ██╔═════╝ ██╔═══██╗ + ${AnsiColor.GREEN} ██║${AnsiColor.RED} ████████║ ██║ ███████╔╝ ╚█████╗ ██║ ██████╗ ███████╔╝ + ${AnsiColor.GREEN}██╗ ██║${AnsiColor.RED} ██╔═══██║ ██║ ██╔════╝ ╚═══██╗ ██║ ██╔═══╝ ██╔══██║ + ${AnsiColor.GREEN}╚██████╔╝${AnsiColor.RED} ██║ ██║ ████████╗ ██║ ██████╔╝ ██║ ████████╗ ██║ ╚██╗ + ${AnsiColor.GREEN} ╚═════╝ ${AnsiColor.RED} ╚═╝ ╚═╝ ╚═══════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══════╝ ╚═╝ ╚═╝ + +${AnsiColor.BRIGHT_BLUE}:: JHipster 🤓 :: Running Spring Boot ${spring-boot.version} :: +:: https://www.jhipster.tech ::${AnsiColor.DEFAULT} diff --git a/src/main/resources/config/application-dev.yml b/src/main/resources/config/application-dev.yml new file mode 100644 index 00000000..fe86d5e5 --- /dev/null +++ b/src/main/resources/config/application-dev.yml @@ -0,0 +1,117 @@ +# =================================================================== +# Spring Boot configuration for the "dev" profile. +# +# This configuration overrides the application.yml file. +# +# More information on profiles: https://www.jhipster.tech/profiles/ +# More information on configuration properties: https://www.jhipster.tech/common-application-properties/ +# =================================================================== + +# =================================================================== +# Standard Spring Boot properties. +# Full reference is available at: +# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html +# =================================================================== + +logging: + level: + ROOT: DEBUG + tech.jhipster: DEBUG + org.hibernate.SQL: DEBUG + org.jhipster.health: DEBUG + +management: + health: + elasticsearch: + enabled: false + +spring: + devtools: + restart: + enabled: true + additional-exclude: static/**,.h2.server.properties + livereload: + enabled: false # we use Webpack dev server + BrowserSync for livereload + jackson: + serialization: + indent-output: true + datasource: + type: com.zaxxer.hikari.HikariDataSource + url: jdbc:h2:file:./build/h2db/db/twentyonepoints;DB_CLOSE_DELAY=-1;MODE=LEGACY + username: TwentyOnePoints + password: + hikari: + poolName: Hikari + auto-commit: false + h2: + console: + # disable spring boot built-in h2-console since we start it manually with correct configuration + enabled: false + jpa: + elasticsearch: + uris: http://localhost:9200 + liquibase: + # Remove 'faker' if you do not want the sample data to be loaded automatically + contexts: dev, faker + mail: + host: localhost + port: 25 + username: + password: + messages: + cache-duration: PT1S # 1 second, see the ISO 8601 standard + thymeleaf: + cache: false + +server: + port: 8080 + +# =================================================================== +# JHipster specific properties +# +# Full reference is available at: https://www.jhipster.tech/common-application-properties/ +# =================================================================== + +jhipster: + cache: # Cache configuration + ehcache: # Ehcache configuration + time-to-live-seconds: 3600 # By default objects stay 1 hour in the cache + max-entries: 100 # Number of objects in each cache entry + # CORS is only enabled by default with the "dev" profile + cors: + # Allow Ionic for JHipster by default (* no longer allowed in Spring Boot 2.4+) + allowed-origins: 'http://localhost:8100,https://localhost:8100,http://localhost:9000,https://localhost:9000,http://localhost:4200,https://localhost:4200' + # Enable CORS when running in GitHub Codespaces + allowed-origin-patterns: 'https://*.githubpreview.dev' + allowed-methods: '*' + allowed-headers: '*' + exposed-headers: 'Authorization,Link,X-Total-Count,X-${jhipster.clientApp.name}-alert,X-${jhipster.clientApp.name}-error,X-${jhipster.clientApp.name}-params' + allow-credentials: true + max-age: 1800 + security: + authentication: + jwt: + # This token must be encoded using Base64 and be at least 256 bits long (you can type `openssl rand -base64 64` on your command line to generate a 512 bits one) + base64-secret: YjAwZDU2ODI3NzNjZjk0NjVhY2UzNGViZmY0YjY1ZGY2MDI5MTc5Y2FlZWYxNzE5Yjg5ZjMxOGZhZTgwZjA5NTFkN2VmNDM3ZGMyZDNjZTViNDAwYTg4NmNlMmM2ZDkxZTMwMDk5YmUxNWNkZmE0YTNiNDlhMGNhZmM4OTk1NmQ= + # Token is valid 24 hours + token-validity-in-seconds: 86400 + token-validity-in-seconds-for-remember-me: 2592000 + mail: # specific JHipster mail property, for standard properties see MailProperties + base-url: http://127.0.0.1:8080 + logging: + use-json-format: false # By default, logs are not in Json format + logstash: # Forward logs to logstash over a socket, used by LoggingConfiguration + enabled: false + host: localhost + port: 5000 + queue-size: 512 +# =================================================================== +# Application specific properties +# Add your own application properties here, see the ApplicationProperties class +# to have type-safe configuration, like in the JHipsterProperties above +# +# More documentation is available at: +# https://www.jhipster.tech/common-application-properties/ +# =================================================================== + +# application: diff --git a/src/main/resources/config/application-prod.yml b/src/main/resources/config/application-prod.yml new file mode 100644 index 00000000..5a4041a6 --- /dev/null +++ b/src/main/resources/config/application-prod.yml @@ -0,0 +1,128 @@ +# =================================================================== +# Spring Boot configuration for the "prod" profile. +# +# This configuration overrides the application.yml file. +# +# More information on profiles: https://www.jhipster.tech/profiles/ +# More information on configuration properties: https://www.jhipster.tech/common-application-properties/ +# =================================================================== + +# =================================================================== +# Standard Spring Boot properties. +# Full reference is available at: +# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html +# =================================================================== + +logging: + level: + ROOT: INFO + tech.jhipster: INFO + org.jhipster.health: INFO + +management: + metrics: + export: + prometheus: + enabled: false + +spring: + devtools: + restart: + enabled: false + livereload: + enabled: false + datasource: + type: com.zaxxer.hikari.HikariDataSource + url: jdbc:postgresql://localhost:5432/TwentyOnePoints + username: TwentyOnePoints + password: + hikari: + poolName: Hikari + auto-commit: false + jpa: + database-platform: tech.jhipster.domain.util.FixedPostgreSQL10Dialect + elasticsearch: + uris: http://localhost:9200 + # Replace by 'prod, faker' to add the faker context and have sample data loaded in production + liquibase: + contexts: prod + mail: + host: localhost + port: 25 + username: + password: + thymeleaf: + cache: true + +# =================================================================== +# To enable TLS in production, generate a certificate using: +# keytool -genkey -alias twentyonepoints -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore keystore.p12 -validity 3650 +# +# You can also use Let's Encrypt: +# See details in topic "Create a Java Keystore (.JKS) from Let's Encrypt Certificates" on https://maximilian-boehm.com/en-gb/blog +# +# Then, modify the server.ssl properties so your "server" configuration looks like: +# +# server: +# port: 443 +# ssl: +# key-store: classpath:config/tls/keystore.p12 +# key-store-password: password +# key-store-type: PKCS12 +# key-alias: selfsigned +# # The ciphers suite enforce the security by deactivating some old and deprecated SSL cipher, this list was tested against SSL Labs (https://www.ssllabs.com/ssltest/) +# ciphers: TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 ,TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 ,TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 ,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384,TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_RSA_WITH_AES_128_CBC_SHA,TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,TLS_DHE_RSA_WITH_AES_256_CBC_SHA,TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_256_GCM_SHA384,TLS_RSA_WITH_AES_128_CBC_SHA256,TLS_RSA_WITH_AES_256_CBC_SHA256,TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA,TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA,TLS_RSA_WITH_CAMELLIA_256_CBC_SHA,TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA,TLS_RSA_WITH_CAMELLIA_128_CBC_SHA +# =================================================================== +server: + port: 8080 + shutdown: graceful # see https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-graceful-shutdown + compression: + enabled: true + mime-types: text/html,text/xml,text/plain,text/css,application/javascript,application/json,image/svg+xml + min-response-size: 1024 + +# =================================================================== +# JHipster specific properties +# +# Full reference is available at: https://www.jhipster.tech/common-application-properties/ +# =================================================================== + +jhipster: + http: + cache: # Used by the CachingHttpHeadersFilter + timeToLiveInDays: 1461 + cache: # Cache configuration + ehcache: # Ehcache configuration + time-to-live-seconds: 3600 # By default objects stay 1 hour in the cache + max-entries: 1000 # Number of objects in each cache entry + security: + authentication: + jwt: + # This token must be encoded using Base64 and be at least 256 bits long (you can type `openssl rand -base64 64` on your command line to generate a 512 bits one) + # As this is the PRODUCTION configuration, you MUST change the default key, and store it securely: + # - In the JHipster Registry (which includes a Spring Cloud Config server) + # - In a separate `application-prod.yml` file, in the same folder as your executable JAR file + # - In the `JHIPSTER_SECURITY_AUTHENTICATION_JWT_BASE64_SECRET` environment variable + base64-secret: YjAwZDU2ODI3NzNjZjk0NjVhY2UzNGViZmY0YjY1ZGY2MDI5MTc5Y2FlZWYxNzE5Yjg5ZjMxOGZhZTgwZjA5NTFkN2VmNDM3ZGMyZDNjZTViNDAwYTg4NmNlMmM2ZDkxZTMwMDk5YmUxNWNkZmE0YTNiNDlhMGNhZmM4OTk1NmQ= + # Token is valid 24 hours + token-validity-in-seconds: 86400 + token-validity-in-seconds-for-remember-me: 2592000 + mail: # specific JHipster mail property, for standard properties see MailProperties + base-url: http://my-server-url-to-change # Modify according to your server's URL + logging: + use-json-format: false # By default, logs are not in Json format + logstash: # Forward logs to logstash over a socket, used by LoggingConfiguration + enabled: false + host: localhost + port: 5000 + queue-size: 512 +# =================================================================== +# Application specific properties +# Add your own application properties here, see the ApplicationProperties class +# to have type-safe configuration, like in the JHipsterProperties above +# +# More documentation is available at: +# https://www.jhipster.tech/common-application-properties/ +# =================================================================== + +# application: diff --git a/src/main/resources/config/application-tls.yml b/src/main/resources/config/application-tls.yml new file mode 100644 index 00000000..039f6f4a --- /dev/null +++ b/src/main/resources/config/application-tls.yml @@ -0,0 +1,19 @@ +# =================================================================== +# Activate this profile to enable TLS and HTTP/2. +# +# JHipster has generated a self-signed certificate, which will be used to encrypt traffic. +# As your browser will not understand this certificate, you will need to import it. +# +# Another (easiest) solution with Chrome is to enable the "allow-insecure-localhost" flag +# at chrome://flags/#allow-insecure-localhost +# =================================================================== +server: + ssl: + key-store: classpath:config/tls/keystore.p12 + key-store-password: password + key-store-type: PKCS12 + key-alias: selfsigned + ciphers: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA + enabled-protocols: TLSv1.2 + http2: + enabled: true diff --git a/src/main/resources/config/application.yml b/src/main/resources/config/application.yml new file mode 100644 index 00000000..20d781a7 --- /dev/null +++ b/src/main/resources/config/application.yml @@ -0,0 +1,246 @@ +# =================================================================== +# Spring Boot configuration. +# +# This configuration will be overridden by the Spring profile you use, +# for example application-dev.yml if you use the "dev" profile. +# +# More information on profiles: https://www.jhipster.tech/profiles/ +# More information on configuration properties: https://www.jhipster.tech/common-application-properties/ +# =================================================================== + +# =================================================================== +# Standard Spring Boot properties. +# Full reference is available at: +# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html +# =================================================================== + +--- +# Conditionally disable springdoc on missing api-docs profile +spring: + config: + activate: + on-profile: '!api-docs' +springdoc: + api-docs: + enabled: false +--- +management: + endpoints: + web: + base-path: /management + exposure: + include: + [ + 'configprops', + 'env', + 'health', + 'info', + 'jhimetrics', + 'jhiopenapigroups', + 'logfile', + 'loggers', + 'prometheus', + 'threaddump', + 'caches', + 'liquibase', + ] + endpoint: + health: + show-details: when_authorized + roles: 'ROLE_ADMIN' + probes: + enabled: true + group: + liveness: + include: livenessState + readiness: + include: readinessState,db + jhimetrics: + enabled: true + info: + git: + mode: full + env: + enabled: true + health: + mail: + enabled: false # When using the MailService, configure an SMTP server and set this to true + metrics: + export: + # Prometheus is the default metrics backend + prometheus: + enabled: true + step: 60 + enable: + http: true + jvm: true + logback: true + process: true + system: true + distribution: + percentiles-histogram: + all: true + percentiles: + all: 0, 0.5, 0.75, 0.95, 0.99, 1.0 + tags: + application: ${spring.application.name} + web: + server: + request: + autotime: + enabled: true + +spring: + application: + name: TwentyOnePoints + profiles: + # The commented value for `active` can be replaced with valid Spring profiles to load. + # Otherwise, it will be filled in by gradle when building the JAR file + # Either way, it can be overridden by `--spring.profiles.active` value passed in the commandline or `-Dspring.profiles.active` set in `JAVA_OPTS` + active: #spring.profiles.active# + group: + dev: + - dev + - api-docs + # Uncomment to activate TLS for the dev profile + #- tls + jmx: + enabled: false + data: + jpa: + repositories: + bootstrap-mode: deferred + jpa: + open-in-view: false + properties: + hibernate.jdbc.time_zone: UTC + hibernate.id.new_generator_mappings: true + hibernate.connection.provider_disables_autocommit: true + hibernate.cache.use_second_level_cache: true + hibernate.cache.use_query_cache: false + hibernate.generate_statistics: false + # modify batch size as necessary + hibernate.jdbc.batch_size: 25 + hibernate.order_inserts: true + hibernate.order_updates: true + hibernate.query.fail_on_pagination_over_collection_fetch: true + hibernate.query.in_clause_parameter_padding: true + hibernate: + ddl-auto: none + naming: + physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy + implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy + messages: + basename: i18n/messages + main: + allow-bean-definition-overriding: true + mvc: + pathmatch: + matching-strategy: ant_path_matcher + task: + execution: + thread-name-prefix: twenty-one-points-task- + pool: + core-size: 2 + max-size: 50 + queue-capacity: 10000 + scheduling: + thread-name-prefix: twenty-one-points-scheduling- + pool: + size: 2 + thymeleaf: + mode: HTML + output: + ansi: + console-available: true + +server: + servlet: + session: + cookie: + http-only: true + +springdoc: + show-actuator: true + +# Properties to be exposed on the /info management endpoint +info: + # Comma separated list of profiles that will trigger the ribbon to show + display-ribbon-on-profiles: 'dev' + +# =================================================================== +# JHipster specific properties +# +# Full reference is available at: https://www.jhipster.tech/common-application-properties/ +# =================================================================== + +jhipster: + clientApp: + name: 'twentyOnePointsApp' + # By default CORS is disabled. Uncomment to enable. + # cors: + # allowed-origins: "http://localhost:8100,http://localhost:9000" + # allowed-methods: "*" + # allowed-headers: "*" + # exposed-headers: "Authorization,Link,X-Total-Count,X-${jhipster.clientApp.name}-alert,X-${jhipster.clientApp.name}-error,X-${jhipster.clientApp.name}-params" + # allow-credentials: true + # max-age: 1800 + mail: + from: TwentyOnePoints@localhost + api-docs: + default-include-pattern: ${server.servlet.context-path:}/api/** + management-include-pattern: ${server.servlet.context-path:}/management/** + title: Twenty One Points API + description: Twenty One Points API documentation + version: 0.0.1 + terms-of-service-url: + contact-name: + contact-url: + contact-email: + license: unlicensed + license-url: + security: + content-security-policy: "default-src 'self'; frame-src 'self' data:; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://storage.googleapis.com; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:" +# =================================================================== +# Application specific properties +# Add your own application properties here, see the ApplicationProperties class +# to have type-safe configuration, like in the JHipsterProperties above +# +# More documentation is available at: +# https://www.jhipster.tech/common-application-properties/ +# =================================================================== + +# application: + +# =================================================================== +# Uncomment or add these properties to your current application properties to +# enable this if you'd like to reindex entities with Elasticsearch +# =================================================================== + +# application: +# elasticsearch: +# reindex-on-startup: true +# =================================================================== +# Uncomment or add these properties to your current application properties to +# enable this if you'd like to reindex entities with Elasticsearch +# =================================================================== + +# application: +# elasticsearch: +# reindex-on-startup: true +# =================================================================== +# Uncomment or add these properties to your current application properties to +# enable this if you'd like to reindex entities with Elasticsearch +# =================================================================== + +# application: +# elasticsearch: +# reindex-on-startup: true +# =================================================================== +# Uncomment or add these properties to your current application properties to +# enable this if you'd like to reindex entities with Elasticsearch +# =================================================================== + +# application: +# elasticsearch: +# reindex-on-startup: true \ No newline at end of file diff --git a/src/main/resources/config/bootstrap-prod.yml b/src/main/resources/config/bootstrap-prod.yml new file mode 100644 index 00000000..1047df3f --- /dev/null +++ b/src/main/resources/config/bootstrap-prod.yml @@ -0,0 +1,6 @@ +# =================================================================== +# Spring Cloud Config bootstrap configuration for the "prod" profile +# =================================================================== + +spring: + cloud: diff --git a/src/main/resources/config/bootstrap.yml b/src/main/resources/config/bootstrap.yml new file mode 100644 index 00000000..c4b2b068 --- /dev/null +++ b/src/main/resources/config/bootstrap.yml @@ -0,0 +1,14 @@ +# =================================================================== +# Spring Cloud Config bootstrap configuration for the "dev" profile +# In prod profile, properties will be overwritten by the ones defined in bootstrap-prod.yml +# =================================================================== + +spring: + application: + name: TwentyOnePoints + profiles: + # The commented value for `active` can be replaced with valid Spring profiles to load. + # Otherwise, it will be filled in by gradle when building the JAR file + # Either way, it can be overridden by `--spring.profiles.active` value passed in the commandline or `-Dspring.profiles.active` set in `JAVA_OPTS` + active: #spring.profiles.active# + cloud: diff --git a/src/main/resources/config/liquibase/changelog/00000000000000_initial_schema.xml b/src/main/resources/config/liquibase/changelog/00000000000000_initial_schema.xml new file mode 100644 index 00000000..eaac648e --- /dev/null +++ b/src/main/resources/config/liquibase/changelog/00000000000000_initial_schema.xml @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/config/liquibase/changelog/20221108000520_added_entity_Points.xml b/src/main/resources/config/liquibase/changelog/20221108000520_added_entity_Points.xml new file mode 100644 index 00000000..6dd9d25b --- /dev/null +++ b/src/main/resources/config/liquibase/changelog/20221108000520_added_entity_Points.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/config/liquibase/changelog/20221108000520_added_entity_constraints_Points.xml b/src/main/resources/config/liquibase/changelog/20221108000520_added_entity_constraints_Points.xml new file mode 100644 index 00000000..5a10479a --- /dev/null +++ b/src/main/resources/config/liquibase/changelog/20221108000520_added_entity_constraints_Points.xml @@ -0,0 +1,17 @@ + + + + + + + + diff --git a/src/main/resources/config/liquibase/changelog/20221108001452_added_entity_Weight.xml b/src/main/resources/config/liquibase/changelog/20221108001452_added_entity_Weight.xml new file mode 100644 index 00000000..438d9c49 --- /dev/null +++ b/src/main/resources/config/liquibase/changelog/20221108001452_added_entity_Weight.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/config/liquibase/changelog/20221108001452_added_entity_constraints_Weight.xml b/src/main/resources/config/liquibase/changelog/20221108001452_added_entity_constraints_Weight.xml new file mode 100644 index 00000000..738b6683 --- /dev/null +++ b/src/main/resources/config/liquibase/changelog/20221108001452_added_entity_constraints_Weight.xml @@ -0,0 +1,17 @@ + + + + + + + + diff --git a/src/main/resources/config/liquibase/changelog/20221108001642_added_entity_BloodPressure.xml b/src/main/resources/config/liquibase/changelog/20221108001642_added_entity_BloodPressure.xml new file mode 100644 index 00000000..fb0256e7 --- /dev/null +++ b/src/main/resources/config/liquibase/changelog/20221108001642_added_entity_BloodPressure.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/config/liquibase/changelog/20221108001642_added_entity_constraints_BloodPressure.xml b/src/main/resources/config/liquibase/changelog/20221108001642_added_entity_constraints_BloodPressure.xml new file mode 100644 index 00000000..042be63d --- /dev/null +++ b/src/main/resources/config/liquibase/changelog/20221108001642_added_entity_constraints_BloodPressure.xml @@ -0,0 +1,17 @@ + + + + + + + + diff --git a/src/main/resources/config/liquibase/changelog/20221108002021_added_entity_Preferences.xml b/src/main/resources/config/liquibase/changelog/20221108002021_added_entity_Preferences.xml new file mode 100644 index 00000000..c5f34cf1 --- /dev/null +++ b/src/main/resources/config/liquibase/changelog/20221108002021_added_entity_Preferences.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/config/liquibase/changelog/20221108002021_added_entity_constraints_Preferences.xml b/src/main/resources/config/liquibase/changelog/20221108002021_added_entity_constraints_Preferences.xml new file mode 100644 index 00000000..39f1b940 --- /dev/null +++ b/src/main/resources/config/liquibase/changelog/20221108002021_added_entity_constraints_Preferences.xml @@ -0,0 +1,17 @@ + + + + + + + + diff --git a/src/main/resources/config/liquibase/data/authority.csv b/src/main/resources/config/liquibase/data/authority.csv new file mode 100644 index 00000000..af5c6dfa --- /dev/null +++ b/src/main/resources/config/liquibase/data/authority.csv @@ -0,0 +1,3 @@ +name +ROLE_ADMIN +ROLE_USER diff --git a/src/main/resources/config/liquibase/data/user.csv b/src/main/resources/config/liquibase/data/user.csv new file mode 100644 index 00000000..fbc52da3 --- /dev/null +++ b/src/main/resources/config/liquibase/data/user.csv @@ -0,0 +1,3 @@ +id;login;password_hash;first_name;last_name;email;image_url;activated;lang_key;created_by;last_modified_by +1;admin;$2a$10$gSAhZrxMllrbgj/kkK9UceBPpChGWJA7SYIb1Mqo.n5aNLq1/oRrC;Administrator;Administrator;admin@localhost;;true;en;system;system +2;user;$2a$10$VEjxo0jq2YG9Rbk2HmX9S.k1uZBGYUHdUcid3g/vfiEl7lwWgOH/K;User;User;user@localhost;;true;en;system;system diff --git a/src/main/resources/config/liquibase/data/user_authority.csv b/src/main/resources/config/liquibase/data/user_authority.csv new file mode 100644 index 00000000..01dbdef7 --- /dev/null +++ b/src/main/resources/config/liquibase/data/user_authority.csv @@ -0,0 +1,4 @@ +user_id;authority_name +1;ROLE_ADMIN +1;ROLE_USER +2;ROLE_USER diff --git a/src/main/resources/config/liquibase/fake-data/blood_pressure.csv b/src/main/resources/config/liquibase/fake-data/blood_pressure.csv new file mode 100644 index 00000000..1761aece --- /dev/null +++ b/src/main/resources/config/liquibase/fake-data/blood_pressure.csv @@ -0,0 +1,11 @@ +id;timestamp;systolic;diastolic +1;2022-11-07T07:09:37;68340;14535 +2;2022-11-07T14:53:32;94757;59470 +3;2022-11-07T04:33:03;29537;85270 +4;2022-11-07T22:37:12;57203;24575 +5;2022-11-07T23:28:59;90887;37312 +6;2022-11-07T08:13:54;79996;12371 +7;2022-11-07T08:34:39;65537;16335 +8;2022-11-07T14:39:25;64136;3779 +9;2022-11-07T14:10:33;63360;82042 +10;2022-11-07T09:10:06;35856;39254 diff --git a/src/main/resources/config/liquibase/fake-data/points.csv b/src/main/resources/config/liquibase/fake-data/points.csv new file mode 100644 index 00000000..4c666786 --- /dev/null +++ b/src/main/resources/config/liquibase/fake-data/points.csv @@ -0,0 +1,11 @@ +id;date;exercise;meals;alcohol;notes +1;2022-11-07;6198;38418;57707;withdrawal Bedfordshire Analyst +2;2022-11-07;2388;1106;9423;Account Garden New +3;2022-11-07;76967;8783;81008;e-business override bandwidth +4;2022-11-07;14053;35227;55062;dynamic Concrete +5;2022-11-07;3838;53842;86262;Drive Research +6;2022-11-07;88201;96453;19830;XSS +7;2022-11-07;45449;51221;53191;interfaces Frozen +8;2022-11-07;98590;58178;44831;program payment +9;2022-11-07;45405;85555;60037;transparent green robust +10;2022-11-07;9401;94414;46576;seize synthesize diff --git a/src/main/resources/config/liquibase/fake-data/preferences.csv b/src/main/resources/config/liquibase/fake-data/preferences.csv new file mode 100644 index 00000000..e5e4d24d --- /dev/null +++ b/src/main/resources/config/liquibase/fake-data/preferences.csv @@ -0,0 +1,11 @@ +id;weekly_goal;weight_units +1;19;LB +2;15;LB +3;19;LB +4;21;KG +5;14;KG +6;11;KG +7;21;LB +8;20;KG +9;15;KG +10;15;LB diff --git a/src/main/resources/config/liquibase/fake-data/weight.csv b/src/main/resources/config/liquibase/fake-data/weight.csv new file mode 100644 index 00000000..8f124f83 --- /dev/null +++ b/src/main/resources/config/liquibase/fake-data/weight.csv @@ -0,0 +1,11 @@ +id;timestamp;weight +1;2022-11-07T09:19:51;32832 +2;2022-11-07T19:14:33;17894 +3;2022-11-07T18:55:08;44583 +4;2022-11-07T22:37:56;2935 +5;2022-11-07T09:19:52;7847 +6;2022-11-07T12:03:37;23548 +7;2022-11-07T19:32:26;8714 +8;2022-11-07T06:29:06;620 +9;2022-11-07T03:01:10;92367 +10;2022-11-07T03:16:38;15689 diff --git a/src/main/resources/config/liquibase/master.xml b/src/main/resources/config/liquibase/master.xml new file mode 100644 index 00000000..5c827d43 --- /dev/null +++ b/src/main/resources/config/liquibase/master.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/main/resources/i18n/messages.properties b/src/main/resources/i18n/messages.properties new file mode 100644 index 00000000..bc22cc44 --- /dev/null +++ b/src/main/resources/i18n/messages.properties @@ -0,0 +1,21 @@ +# Error page +error.title=Your request cannot be processed +error.subtitle=Sorry, an error has occurred. +error.status=Status: +error.message=Message: + +# Activation email +email.activation.title=TwentyOnePoints account activation is required +email.activation.greeting=Dear {0} +email.activation.text1=Your TwentyOnePoints account has been created, please click on the URL below to activate it: +email.activation.text2=Regards, +email.signature=TwentyOnePoints Team. + +# Creation email +email.creation.text1=Your TwentyOnePoints account has been created, please click on the URL below to access it: + +# Reset email +email.reset.title=TwentyOnePoints password reset +email.reset.greeting=Dear {0} +email.reset.text1=For your TwentyOnePoints account a password reset was requested, please click on the URL below to reset it: +email.reset.text2=Regards, diff --git a/src/main/resources/i18n/messages_en.properties b/src/main/resources/i18n/messages_en.properties new file mode 100644 index 00000000..1bed8fdd --- /dev/null +++ b/src/main/resources/i18n/messages_en.properties @@ -0,0 +1,21 @@ +# Error page +error.title=Your request cannot be processed +error.subtitle=Sorry, an error has occurred. +error.status=Status: +error.message=Message: + +# Activation email +email.activation.title=TwentyOnePoints account activation +email.activation.greeting=Dear {0} +email.activation.text1=Your TwentyOnePoints account has been created, please click on the URL below to activate it: +email.activation.text2=Regards, +email.signature=TwentyOnePoints Team. + +# Creation email +email.creation.text1=Your TwentyOnePoints account has been created, please click on the URL below to access it: + +# Reset email +email.reset.title=TwentyOnePoints password reset +email.reset.greeting=Dear {0} +email.reset.text1=For your TwentyOnePoints account a password reset was requested, please click on the URL below to reset it: +email.reset.text2=Regards, diff --git a/src/main/resources/i18n/messages_fr.properties b/src/main/resources/i18n/messages_fr.properties new file mode 100644 index 00000000..ff36d37e --- /dev/null +++ b/src/main/resources/i18n/messages_fr.properties @@ -0,0 +1,21 @@ +# Error page +error.title=Votre demande ne peut être traitée +error.subtitle=Désolé, une erreur s'est produite. +error.status=Statut : +error.message=Message : + +# Activation email +email.activation.title=Activation de votre compte TwentyOnePoints +email.activation.greeting=Cher {0} +email.activation.text1=Votre compte TwentyOnePoints a été créé, pour l'activer merci de cliquer sur le lien ci-dessous : +email.activation.text2=Cordialement, +email.signature=TwentyOnePoints. + +# Creation email +email.creation.text1=Votre compte TwentyOnePoints a été créé, merci de cliquer sur le lien ci-dessous pour y accéder : + +# Reset email +email.reset.title=TwentyOnePoints Réinitialisation de mot de passe +email.reset.greeting=Cher {0} +email.reset.text1=Un nouveau mot de passe pour votre compte TwentyOnePoints a été demandé, veuillez cliquer sur le lien ci-dessous pour le réinitialiser : +email.reset.text2=Cordialement, diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml new file mode 100644 index 00000000..c5ccc030 --- /dev/null +++ b/src/main/resources/logback-spring.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + + + diff --git a/src/main/resources/templates/error.html b/src/main/resources/templates/error.html new file mode 100644 index 00000000..690e8560 --- /dev/null +++ b/src/main/resources/templates/error.html @@ -0,0 +1,92 @@ + + + + + + Your request cannot be processed + + + +

+

Your request cannot be processed :(

+ +

Sorry, an error has occurred.

+ + Status:  ()
+ + Message: 
+
+
+ + diff --git a/src/main/resources/templates/mail/activationEmail.html b/src/main/resources/templates/mail/activationEmail.html new file mode 100644 index 00000000..0bb53a28 --- /dev/null +++ b/src/main/resources/templates/mail/activationEmail.html @@ -0,0 +1,20 @@ + + + + JHipster activation + + + + +

Dear

+

Your JHipster account has been created, please click on the URL below to activate it:

+

+ Activation link +

+

+ Regards, +
+ JHipster. +

+ + diff --git a/src/main/resources/templates/mail/creationEmail.html b/src/main/resources/templates/mail/creationEmail.html new file mode 100644 index 00000000..4e528980 --- /dev/null +++ b/src/main/resources/templates/mail/creationEmail.html @@ -0,0 +1,20 @@ + + + + JHipster creation + + + + +

Dear

+

Your JHipster account has been created, please click on the URL below to access it:

+

+ Login link +

+

+ Regards, +
+ JHipster. +

+ + diff --git a/src/main/resources/templates/mail/passwordResetEmail.html b/src/main/resources/templates/mail/passwordResetEmail.html new file mode 100644 index 00000000..290ca6dc --- /dev/null +++ b/src/main/resources/templates/mail/passwordResetEmail.html @@ -0,0 +1,22 @@ + + + + JHipster password reset + + + + +

Dear

+

+ For your JHipster account a password reset was requested, please click on the URL below to reset it: +

+

+ Login link +

+

+ Regards, +
+ JHipster. +

+ + diff --git a/src/main/webapp/404.html b/src/main/webapp/404.html new file mode 100644 index 00000000..7569d7e2 --- /dev/null +++ b/src/main/webapp/404.html @@ -0,0 +1,58 @@ + + + + + Page Not Found + + + + + +

Page Not Found

+

Sorry, but the page you were trying to view does not exist.

+ + + diff --git a/src/main/webapp/WEB-INF/web.xml b/src/main/webapp/WEB-INF/web.xml new file mode 100644 index 00000000..f1611b51 --- /dev/null +++ b/src/main/webapp/WEB-INF/web.xml @@ -0,0 +1,13 @@ + + + + + html + text/html;charset=utf-8 + + + diff --git a/src/main/webapp/app/account/account.module.ts b/src/main/webapp/app/account/account.module.ts new file mode 100644 index 00000000..060a020a --- /dev/null +++ b/src/main/webapp/app/account/account.module.ts @@ -0,0 +1,26 @@ +import { NgModule } from '@angular/core'; +import { RouterModule } from '@angular/router'; + +import { SharedModule } from 'app/shared/shared.module'; +import { PasswordStrengthBarComponent } from './password/password-strength-bar/password-strength-bar.component'; +import { RegisterComponent } from './register/register.component'; +import { ActivateComponent } from './activate/activate.component'; +import { PasswordComponent } from './password/password.component'; +import { PasswordResetInitComponent } from './password-reset/init/password-reset-init.component'; +import { PasswordResetFinishComponent } from './password-reset/finish/password-reset-finish.component'; +import { SettingsComponent } from './settings/settings.component'; +import { accountState } from './account.route'; + +@NgModule({ + imports: [SharedModule, RouterModule.forChild(accountState)], + declarations: [ + ActivateComponent, + RegisterComponent, + PasswordComponent, + PasswordStrengthBarComponent, + PasswordResetInitComponent, + PasswordResetFinishComponent, + SettingsComponent, + ], +}) +export class AccountModule {} diff --git a/src/main/webapp/app/account/account.route.ts b/src/main/webapp/app/account/account.route.ts new file mode 100644 index 00000000..1cbfb9d0 --- /dev/null +++ b/src/main/webapp/app/account/account.route.ts @@ -0,0 +1,17 @@ +import { Routes } from '@angular/router'; + +import { activateRoute } from './activate/activate.route'; +import { passwordRoute } from './password/password.route'; +import { passwordResetFinishRoute } from './password-reset/finish/password-reset-finish.route'; +import { passwordResetInitRoute } from './password-reset/init/password-reset-init.route'; +import { registerRoute } from './register/register.route'; +import { settingsRoute } from './settings/settings.route'; + +const ACCOUNT_ROUTES = [activateRoute, passwordRoute, passwordResetFinishRoute, passwordResetInitRoute, registerRoute, settingsRoute]; + +export const accountState: Routes = [ + { + path: '', + children: ACCOUNT_ROUTES, + }, +]; diff --git a/src/main/webapp/app/account/activate/activate.component.html b/src/main/webapp/app/account/activate/activate.component.html new file mode 100644 index 00000000..4cd58373 --- /dev/null +++ b/src/main/webapp/app/account/activate/activate.component.html @@ -0,0 +1,16 @@ +
+
+
+

Activation

+ +
+ Your user account has been activated. Please + sign in. +
+ +
+ Your user could not be activated. Please use the registration form to sign up. +
+
+
+
diff --git a/src/main/webapp/app/account/activate/activate.component.spec.ts b/src/main/webapp/app/account/activate/activate.component.spec.ts new file mode 100644 index 00000000..87f6797a --- /dev/null +++ b/src/main/webapp/app/account/activate/activate.component.spec.ts @@ -0,0 +1,69 @@ +import { TestBed, waitForAsync, tick, fakeAsync, inject } from '@angular/core/testing'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { ActivatedRoute } from '@angular/router'; +import { of, throwError } from 'rxjs'; + +import { ActivateService } from './activate.service'; +import { ActivateComponent } from './activate.component'; + +describe('ActivateComponent', () => { + let comp: ActivateComponent; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + declarations: [ActivateComponent], + providers: [ + { + provide: ActivatedRoute, + useValue: { queryParams: of({ key: 'ABC123' }) }, + }, + ], + }) + .overrideTemplate(ActivateComponent, '') + .compileComponents(); + })); + + beforeEach(() => { + const fixture = TestBed.createComponent(ActivateComponent); + comp = fixture.componentInstance; + }); + + it('calls activate.get with the key from params', inject( + [ActivateService], + fakeAsync((service: ActivateService) => { + jest.spyOn(service, 'get').mockReturnValue(of()); + + comp.ngOnInit(); + tick(); + + expect(service.get).toHaveBeenCalledWith('ABC123'); + }) + )); + + it('should set set success to true upon successful activation', inject( + [ActivateService], + fakeAsync((service: ActivateService) => { + jest.spyOn(service, 'get').mockReturnValue(of({})); + + comp.ngOnInit(); + tick(); + + expect(comp.error).toBe(false); + expect(comp.success).toBe(true); + }) + )); + + it('should set set error to true upon activation failure', inject( + [ActivateService], + fakeAsync((service: ActivateService) => { + jest.spyOn(service, 'get').mockReturnValue(throwError('ERROR')); + + comp.ngOnInit(); + tick(); + + expect(comp.error).toBe(true); + expect(comp.success).toBe(false); + }) + )); +}); diff --git a/src/main/webapp/app/account/activate/activate.component.ts b/src/main/webapp/app/account/activate/activate.component.ts new file mode 100644 index 00000000..e1bd9640 --- /dev/null +++ b/src/main/webapp/app/account/activate/activate.component.ts @@ -0,0 +1,23 @@ +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { mergeMap } from 'rxjs/operators'; + +import { ActivateService } from './activate.service'; + +@Component({ + selector: 'jhi-activate', + templateUrl: './activate.component.html', +}) +export class ActivateComponent implements OnInit { + error = false; + success = false; + + constructor(private activateService: ActivateService, private route: ActivatedRoute) {} + + ngOnInit(): void { + this.route.queryParams.pipe(mergeMap(params => this.activateService.get(params.key))).subscribe({ + next: () => (this.success = true), + error: () => (this.error = true), + }); + } +} diff --git a/src/main/webapp/app/account/activate/activate.route.ts b/src/main/webapp/app/account/activate/activate.route.ts new file mode 100644 index 00000000..8da1b6d5 --- /dev/null +++ b/src/main/webapp/app/account/activate/activate.route.ts @@ -0,0 +1,11 @@ +import { Route } from '@angular/router'; + +import { ActivateComponent } from './activate.component'; + +export const activateRoute: Route = { + path: 'activate', + component: ActivateComponent, + data: { + pageTitle: 'activate.title', + }, +}; diff --git a/src/main/webapp/app/account/activate/activate.service.spec.ts b/src/main/webapp/app/account/activate/activate.service.spec.ts new file mode 100644 index 00000000..9acfc772 --- /dev/null +++ b/src/main/webapp/app/account/activate/activate.service.spec.ts @@ -0,0 +1,47 @@ +import { TestBed } from '@angular/core/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; + +import { ActivateService } from './activate.service'; +import { ApplicationConfigService } from 'app/core/config/application-config.service'; + +describe('ActivateService Service', () => { + let service: ActivateService; + let httpMock: HttpTestingController; + let applicationConfigService: ApplicationConfigService; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + }); + + service = TestBed.inject(ActivateService); + applicationConfigService = TestBed.inject(ApplicationConfigService); + httpMock = TestBed.inject(HttpTestingController); + }); + + afterEach(() => { + httpMock.verify(); + }); + + describe('Service methods', () => { + it('should call api/activate endpoint with correct values', () => { + // GIVEN + let expectedResult; + const key = 'key'; + const value = true; + + // WHEN + service.get(key).subscribe(received => { + expectedResult = received; + }); + const testRequest = httpMock.expectOne({ + method: 'GET', + url: applicationConfigService.getEndpointFor(`api/activate?key=${key}`), + }); + testRequest.flush(value); + + // THEN + expect(expectedResult).toEqual(value); + }); + }); +}); diff --git a/src/main/webapp/app/account/activate/activate.service.ts b/src/main/webapp/app/account/activate/activate.service.ts new file mode 100644 index 00000000..d6969d57 --- /dev/null +++ b/src/main/webapp/app/account/activate/activate.service.ts @@ -0,0 +1,16 @@ +import { Injectable } from '@angular/core'; +import { HttpClient, HttpParams } from '@angular/common/http'; +import { Observable } from 'rxjs'; + +import { ApplicationConfigService } from 'app/core/config/application-config.service'; + +@Injectable({ providedIn: 'root' }) +export class ActivateService { + constructor(private http: HttpClient, private applicationConfigService: ApplicationConfigService) {} + + get(key: string): Observable<{}> { + return this.http.get(this.applicationConfigService.getEndpointFor('api/activate'), { + params: new HttpParams().set('key', key), + }); + } +} diff --git a/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.html b/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.html new file mode 100644 index 00000000..493ef8f9 --- /dev/null +++ b/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.html @@ -0,0 +1,137 @@ +
+
+
+

Reset password

+ +
+ The reset key is missing. +
+ +
+ Choose a new password +
+ +
+ Your password couldn't be reset. Remember a password request is only valid for 24 hours. +
+ +
+ Your password has been reset. Please + sign in. +
+ +
+ The password and its confirmation do not match! +
+ +
+
+
+ + + +
+ + Your password is required. + + + + Your password is required to be at least 4 characters. + + + + Your password cannot be longer than 50 characters. + +
+ + +
+ +
+ + + +
+ + Your confirmation password is required. + + + + Your confirmation password is required to be at least 4 characters. + + + + Your confirmation password cannot be longer than 50 characters. + +
+
+ + +
+
+
+
+
diff --git a/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.spec.ts b/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.spec.ts new file mode 100644 index 00000000..7fb9c6af --- /dev/null +++ b/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.spec.ts @@ -0,0 +1,98 @@ +import { ElementRef } from '@angular/core'; +import { ComponentFixture, TestBed, inject, tick, fakeAsync } from '@angular/core/testing'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { FormBuilder } from '@angular/forms'; +import { ActivatedRoute } from '@angular/router'; +import { of, throwError } from 'rxjs'; + +import { PasswordResetFinishComponent } from './password-reset-finish.component'; +import { PasswordResetFinishService } from './password-reset-finish.service'; + +describe('PasswordResetFinishComponent', () => { + let fixture: ComponentFixture; + let comp: PasswordResetFinishComponent; + + beforeEach(() => { + fixture = TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + declarations: [PasswordResetFinishComponent], + providers: [ + FormBuilder, + { + provide: ActivatedRoute, + useValue: { queryParams: of({ key: 'XYZPDQ' }) }, + }, + ], + }) + .overrideTemplate(PasswordResetFinishComponent, '') + .createComponent(PasswordResetFinishComponent); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(PasswordResetFinishComponent); + comp = fixture.componentInstance; + comp.ngOnInit(); + }); + + it('should define its initial state', () => { + expect(comp.initialized).toBe(true); + expect(comp.key).toEqual('XYZPDQ'); + }); + + it('sets focus after the view has been initialized', () => { + const node = { + focus: jest.fn(), + }; + comp.newPassword = new ElementRef(node); + + comp.ngAfterViewInit(); + + expect(node.focus).toHaveBeenCalled(); + }); + + it('should ensure the two passwords entered match', () => { + comp.passwordForm.patchValue({ + newPassword: 'password', + confirmPassword: 'non-matching', + }); + + comp.finishReset(); + + expect(comp.doNotMatch).toBe(true); + }); + + it('should update success to true after resetting password', inject( + [PasswordResetFinishService], + fakeAsync((service: PasswordResetFinishService) => { + jest.spyOn(service, 'save').mockReturnValue(of({})); + comp.passwordForm.patchValue({ + newPassword: 'password', + confirmPassword: 'password', + }); + + comp.finishReset(); + tick(); + + expect(service.save).toHaveBeenCalledWith('XYZPDQ', 'password'); + expect(comp.success).toBe(true); + }) + )); + + it('should notify of generic error', inject( + [PasswordResetFinishService], + fakeAsync((service: PasswordResetFinishService) => { + jest.spyOn(service, 'save').mockReturnValue(throwError('ERROR')); + comp.passwordForm.patchValue({ + newPassword: 'password', + confirmPassword: 'password', + }); + + comp.finishReset(); + tick(); + + expect(service.save).toHaveBeenCalledWith('XYZPDQ', 'password'); + expect(comp.success).toBe(false); + expect(comp.error).toBe(true); + }) + )); +}); diff --git a/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.ts b/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.ts new file mode 100644 index 00000000..cf0eaefd --- /dev/null +++ b/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.ts @@ -0,0 +1,64 @@ +import { Component, OnInit, AfterViewInit, ElementRef, ViewChild } from '@angular/core'; +import { FormGroup, FormControl, Validators } from '@angular/forms'; +import { ActivatedRoute } from '@angular/router'; + +import { PasswordResetFinishService } from './password-reset-finish.service'; + +@Component({ + selector: 'jhi-password-reset-finish', + templateUrl: './password-reset-finish.component.html', +}) +export class PasswordResetFinishComponent implements OnInit, AfterViewInit { + @ViewChild('newPassword', { static: false }) + newPassword?: ElementRef; + + initialized = false; + doNotMatch = false; + error = false; + success = false; + key = ''; + + passwordForm = new FormGroup({ + newPassword: new FormControl('', { + nonNullable: true, + validators: [Validators.required, Validators.minLength(4), Validators.maxLength(50)], + }), + confirmPassword: new FormControl('', { + nonNullable: true, + validators: [Validators.required, Validators.minLength(4), Validators.maxLength(50)], + }), + }); + + constructor(private passwordResetFinishService: PasswordResetFinishService, private route: ActivatedRoute) {} + + ngOnInit(): void { + this.route.queryParams.subscribe(params => { + if (params['key']) { + this.key = params['key']; + } + this.initialized = true; + }); + } + + ngAfterViewInit(): void { + if (this.newPassword) { + this.newPassword.nativeElement.focus(); + } + } + + finishReset(): void { + this.doNotMatch = false; + this.error = false; + + const { newPassword, confirmPassword } = this.passwordForm.getRawValue(); + + if (newPassword !== confirmPassword) { + this.doNotMatch = true; + } else { + this.passwordResetFinishService.save(this.key, newPassword).subscribe({ + next: () => (this.success = true), + error: () => (this.error = true), + }); + } + } +} diff --git a/src/main/webapp/app/account/password-reset/finish/password-reset-finish.route.ts b/src/main/webapp/app/account/password-reset/finish/password-reset-finish.route.ts new file mode 100644 index 00000000..3786df74 --- /dev/null +++ b/src/main/webapp/app/account/password-reset/finish/password-reset-finish.route.ts @@ -0,0 +1,11 @@ +import { Route } from '@angular/router'; + +import { PasswordResetFinishComponent } from './password-reset-finish.component'; + +export const passwordResetFinishRoute: Route = { + path: 'reset/finish', + component: PasswordResetFinishComponent, + data: { + pageTitle: 'global.menu.account.password', + }, +}; diff --git a/src/main/webapp/app/account/password-reset/finish/password-reset-finish.service.spec.ts b/src/main/webapp/app/account/password-reset/finish/password-reset-finish.service.spec.ts new file mode 100644 index 00000000..fbc81570 --- /dev/null +++ b/src/main/webapp/app/account/password-reset/finish/password-reset-finish.service.spec.ts @@ -0,0 +1,44 @@ +import { TestBed } from '@angular/core/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; + +import { PasswordResetFinishService } from './password-reset-finish.service'; +import { ApplicationConfigService } from 'app/core/config/application-config.service'; + +describe('PasswordResetFinish Service', () => { + let service: PasswordResetFinishService; + let httpMock: HttpTestingController; + let applicationConfigService: ApplicationConfigService; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + }); + + service = TestBed.inject(PasswordResetFinishService); + applicationConfigService = TestBed.inject(ApplicationConfigService); + httpMock = TestBed.inject(HttpTestingController); + }); + + afterEach(() => { + httpMock.verify(); + }); + + describe('Service methods', () => { + it('should call reset-password/finish endpoint with correct values', () => { + // GIVEN + const key = 'abc'; + const newPassword = 'password'; + + // WHEN + service.save(key, newPassword).subscribe(); + + const testRequest = httpMock.expectOne({ + method: 'POST', + url: applicationConfigService.getEndpointFor('api/account/reset-password/finish'), + }); + + // THEN + expect(testRequest.request.body).toEqual({ key, newPassword }); + }); + }); +}); diff --git a/src/main/webapp/app/account/password-reset/finish/password-reset-finish.service.ts b/src/main/webapp/app/account/password-reset/finish/password-reset-finish.service.ts new file mode 100644 index 00000000..1dff3243 --- /dev/null +++ b/src/main/webapp/app/account/password-reset/finish/password-reset-finish.service.ts @@ -0,0 +1,14 @@ +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { Observable } from 'rxjs'; + +import { ApplicationConfigService } from 'app/core/config/application-config.service'; + +@Injectable({ providedIn: 'root' }) +export class PasswordResetFinishService { + constructor(private http: HttpClient, private applicationConfigService: ApplicationConfigService) {} + + save(key: string, newPassword: string): Observable<{}> { + return this.http.post(this.applicationConfigService.getEndpointFor('api/account/reset-password/finish'), { key, newPassword }); + } +} diff --git a/src/main/webapp/app/account/password-reset/init/password-reset-init.component.html b/src/main/webapp/app/account/password-reset/init/password-reset-init.component.html new file mode 100644 index 00000000..301a96e9 --- /dev/null +++ b/src/main/webapp/app/account/password-reset/init/password-reset-init.component.html @@ -0,0 +1,81 @@ +
+
+
+

Reset your password

+ + + +
+ Enter the email address you used to register +
+ +
+ Check your emails for details on how to reset your password. +
+ +
+
+ + + +
+ + Your email is required. + + + + Your email is invalid. + + + + Your email is required to be at least 5 characters. + + + + Your email cannot be longer than 50 characters. + +
+
+ + +
+
+
+
diff --git a/src/main/webapp/app/account/password-reset/init/password-reset-init.component.spec.ts b/src/main/webapp/app/account/password-reset/init/password-reset-init.component.spec.ts new file mode 100644 index 00000000..b9b6ebe4 --- /dev/null +++ b/src/main/webapp/app/account/password-reset/init/password-reset-init.component.spec.ts @@ -0,0 +1,63 @@ +import { ElementRef } from '@angular/core'; +import { ComponentFixture, TestBed, inject } from '@angular/core/testing'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { FormBuilder } from '@angular/forms'; +import { of, throwError } from 'rxjs'; + +import { PasswordResetInitComponent } from './password-reset-init.component'; +import { PasswordResetInitService } from './password-reset-init.service'; + +describe('PasswordResetInitComponent', () => { + let fixture: ComponentFixture; + let comp: PasswordResetInitComponent; + + beforeEach(() => { + fixture = TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + declarations: [PasswordResetInitComponent], + providers: [FormBuilder], + }) + .overrideTemplate(PasswordResetInitComponent, '') + .createComponent(PasswordResetInitComponent); + comp = fixture.componentInstance; + }); + + it('sets focus after the view has been initialized', () => { + const node = { + focus: jest.fn(), + }; + comp.email = new ElementRef(node); + + comp.ngAfterViewInit(); + + expect(node.focus).toHaveBeenCalled(); + }); + + it('notifies of success upon successful requestReset', inject([PasswordResetInitService], (service: PasswordResetInitService) => { + jest.spyOn(service, 'save').mockReturnValue(of({})); + comp.resetRequestForm.patchValue({ + email: 'user@domain.com', + }); + + comp.requestReset(); + + expect(service.save).toHaveBeenCalledWith('user@domain.com'); + expect(comp.success).toBe(true); + })); + + it('no notification of success upon error response', inject([PasswordResetInitService], (service: PasswordResetInitService) => { + jest.spyOn(service, 'save').mockReturnValue( + throwError({ + status: 503, + data: 'something else', + }) + ); + comp.resetRequestForm.patchValue({ + email: 'user@domain.com', + }); + comp.requestReset(); + + expect(service.save).toHaveBeenCalledWith('user@domain.com'); + expect(comp.success).toBe(false); + })); +}); diff --git a/src/main/webapp/app/account/password-reset/init/password-reset-init.component.ts b/src/main/webapp/app/account/password-reset/init/password-reset-init.component.ts new file mode 100644 index 00000000..ec99fcda --- /dev/null +++ b/src/main/webapp/app/account/password-reset/init/password-reset-init.component.ts @@ -0,0 +1,30 @@ +import { Component, AfterViewInit, ElementRef, ViewChild } from '@angular/core'; +import { FormBuilder, Validators } from '@angular/forms'; + +import { PasswordResetInitService } from './password-reset-init.service'; + +@Component({ + selector: 'jhi-password-reset-init', + templateUrl: './password-reset-init.component.html', +}) +export class PasswordResetInitComponent implements AfterViewInit { + @ViewChild('email', { static: false }) + email?: ElementRef; + + success = false; + resetRequestForm = this.fb.group({ + email: ['', [Validators.required, Validators.minLength(5), Validators.maxLength(254), Validators.email]], + }); + + constructor(private passwordResetInitService: PasswordResetInitService, private fb: FormBuilder) {} + + ngAfterViewInit(): void { + if (this.email) { + this.email.nativeElement.focus(); + } + } + + requestReset(): void { + this.passwordResetInitService.save(this.resetRequestForm.get(['email'])!.value).subscribe(() => (this.success = true)); + } +} diff --git a/src/main/webapp/app/account/password-reset/init/password-reset-init.route.ts b/src/main/webapp/app/account/password-reset/init/password-reset-init.route.ts new file mode 100644 index 00000000..6ba92a41 --- /dev/null +++ b/src/main/webapp/app/account/password-reset/init/password-reset-init.route.ts @@ -0,0 +1,11 @@ +import { Route } from '@angular/router'; + +import { PasswordResetInitComponent } from './password-reset-init.component'; + +export const passwordResetInitRoute: Route = { + path: 'reset/request', + component: PasswordResetInitComponent, + data: { + pageTitle: 'global.menu.account.password', + }, +}; diff --git a/src/main/webapp/app/account/password-reset/init/password-reset-init.service.spec.ts b/src/main/webapp/app/account/password-reset/init/password-reset-init.service.spec.ts new file mode 100644 index 00000000..bb0995e6 --- /dev/null +++ b/src/main/webapp/app/account/password-reset/init/password-reset-init.service.spec.ts @@ -0,0 +1,43 @@ +import { TestBed } from '@angular/core/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; + +import { PasswordResetInitService } from './password-reset-init.service'; +import { ApplicationConfigService } from 'app/core/config/application-config.service'; + +describe('PasswordResetInit Service', () => { + let service: PasswordResetInitService; + let httpMock: HttpTestingController; + let applicationConfigService: ApplicationConfigService; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + }); + + service = TestBed.inject(PasswordResetInitService); + applicationConfigService = TestBed.inject(ApplicationConfigService); + httpMock = TestBed.inject(HttpTestingController); + }); + + afterEach(() => { + httpMock.verify(); + }); + + describe('Service methods', () => { + it('should call reset-password/init endpoint with correct values', () => { + // GIVEN + const mail = 'test@test.com'; + + // WHEN + service.save(mail).subscribe(); + + const testRequest = httpMock.expectOne({ + method: 'POST', + url: applicationConfigService.getEndpointFor('api/account/reset-password/init'), + }); + + // THEN + expect(testRequest.request.body).toEqual(mail); + }); + }); +}); diff --git a/src/main/webapp/app/account/password-reset/init/password-reset-init.service.ts b/src/main/webapp/app/account/password-reset/init/password-reset-init.service.ts new file mode 100644 index 00000000..8ff05e07 --- /dev/null +++ b/src/main/webapp/app/account/password-reset/init/password-reset-init.service.ts @@ -0,0 +1,14 @@ +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { Observable } from 'rxjs'; + +import { ApplicationConfigService } from 'app/core/config/application-config.service'; + +@Injectable({ providedIn: 'root' }) +export class PasswordResetInitService { + constructor(private http: HttpClient, private applicationConfigService: ApplicationConfigService) {} + + save(mail: string): Observable<{}> { + return this.http.post(this.applicationConfigService.getEndpointFor('api/account/reset-password/init'), mail); + } +} diff --git a/src/main/webapp/app/account/password/password-strength-bar/password-strength-bar.component.html b/src/main/webapp/app/account/password/password-strength-bar/password-strength-bar.component.html new file mode 100644 index 00000000..244da088 --- /dev/null +++ b/src/main/webapp/app/account/password/password-strength-bar/password-strength-bar.component.html @@ -0,0 +1,10 @@ +
+ Password strength: +
    +
  • +
  • +
  • +
  • +
  • +
+
diff --git a/src/main/webapp/app/account/password/password-strength-bar/password-strength-bar.component.scss b/src/main/webapp/app/account/password/password-strength-bar/password-strength-bar.component.scss new file mode 100644 index 00000000..67ce4687 --- /dev/null +++ b/src/main/webapp/app/account/password/password-strength-bar/password-strength-bar.component.scss @@ -0,0 +1,23 @@ +/* ========================================================================== +start Password strength bar style +========================================================================== */ +ul#strength { + display: inline; + list-style: none; + margin: 0; + margin-left: 15px; + padding: 0; + vertical-align: 2px; +} + +.point { + background: #ddd; + border-radius: 2px; + display: inline-block; + height: 5px; + margin-right: 1px; + width: 20px; + &:last-child { + margin: 0 !important; + } +} diff --git a/src/main/webapp/app/account/password/password-strength-bar/password-strength-bar.component.spec.ts b/src/main/webapp/app/account/password/password-strength-bar/password-strength-bar.component.spec.ts new file mode 100644 index 00000000..0ff9dbda --- /dev/null +++ b/src/main/webapp/app/account/password/password-strength-bar/password-strength-bar.component.spec.ts @@ -0,0 +1,46 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; + +import { PasswordStrengthBarComponent } from './password-strength-bar.component'; + +describe('PasswordStrengthBarComponent', () => { + let comp: PasswordStrengthBarComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [PasswordStrengthBarComponent], + }) + .overrideTemplate(PasswordStrengthBarComponent, '') + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(PasswordStrengthBarComponent); + comp = fixture.componentInstance; + }); + + describe('PasswordStrengthBarComponents', () => { + it('should initialize with default values', () => { + expect(comp.measureStrength('')).toBe(0); + expect(comp.colors).toEqual(['#F00', '#F90', '#FF0', '#9F0', '#0F0']); + expect(comp.getColor(0).idx).toBe(1); + expect(comp.getColor(0).color).toBe(comp.colors[0]); + }); + + it('should increase strength upon password value change', () => { + expect(comp.measureStrength('')).toBe(0); + expect(comp.measureStrength('aa')).toBeGreaterThanOrEqual(comp.measureStrength('')); + expect(comp.measureStrength('aa^6')).toBeGreaterThanOrEqual(comp.measureStrength('aa')); + expect(comp.measureStrength('Aa090(**)')).toBeGreaterThanOrEqual(comp.measureStrength('aa^6')); + expect(comp.measureStrength('Aa090(**)+-07365')).toBeGreaterThanOrEqual(comp.measureStrength('Aa090(**)')); + }); + + it('should change the color based on strength', () => { + expect(comp.getColor(0).color).toBe(comp.colors[0]); + expect(comp.getColor(11).color).toBe(comp.colors[1]); + expect(comp.getColor(22).color).toBe(comp.colors[2]); + expect(comp.getColor(33).color).toBe(comp.colors[3]); + expect(comp.getColor(44).color).toBe(comp.colors[4]); + }); + }); +}); diff --git a/src/main/webapp/app/account/password/password-strength-bar/password-strength-bar.component.ts b/src/main/webapp/app/account/password/password-strength-bar/password-strength-bar.component.ts new file mode 100644 index 00000000..34cb8db9 --- /dev/null +++ b/src/main/webapp/app/account/password/password-strength-bar/password-strength-bar.component.ts @@ -0,0 +1,72 @@ +import { Component, ElementRef, Input, Renderer2 } from '@angular/core'; + +@Component({ + selector: 'jhi-password-strength-bar', + templateUrl: './password-strength-bar.component.html', + styleUrls: ['./password-strength-bar.component.scss'], +}) +export class PasswordStrengthBarComponent { + colors = ['#F00', '#F90', '#FF0', '#9F0', '#0F0']; + + constructor(private renderer: Renderer2, private elementRef: ElementRef) {} + + measureStrength(p: string): number { + let force = 0; + const regex = /[$-/:-?{-~!"^_`[\]]/g; // " + const lowerLetters = /[a-z]+/.test(p); + const upperLetters = /[A-Z]+/.test(p); + const numbers = /\d+/.test(p); + const symbols = regex.test(p); + + const flags = [lowerLetters, upperLetters, numbers, symbols]; + const passedMatches = flags.filter((isMatchedFlag: boolean) => isMatchedFlag === true).length; + + force += 2 * p.length + (p.length >= 10 ? 1 : 0); + force += passedMatches * 10; + + // penalty (short password) + force = p.length <= 6 ? Math.min(force, 10) : force; + + // penalty (poor variety of characters) + force = passedMatches === 1 ? Math.min(force, 10) : force; + force = passedMatches === 2 ? Math.min(force, 20) : force; + force = passedMatches === 3 ? Math.min(force, 40) : force; + + return force; + } + + getColor(s: number): { idx: number; color: string } { + let idx = 0; + if (s > 10) { + if (s <= 20) { + idx = 1; + } else if (s <= 30) { + idx = 2; + } else if (s <= 40) { + idx = 3; + } else { + idx = 4; + } + } + return { idx: idx + 1, color: this.colors[idx] }; + } + + @Input() + set passwordToCheck(password: string) { + if (password) { + const c = this.getColor(this.measureStrength(password)); + const element = this.elementRef.nativeElement; + if (element.className) { + this.renderer.removeClass(element, element.className); + } + const lis = element.getElementsByTagName('li'); + for (let i = 0; i < lis.length; i++) { + if (i < c.idx) { + this.renderer.setStyle(lis[i], 'backgroundColor', c.color); + } else { + this.renderer.setStyle(lis[i], 'backgroundColor', '#DDD'); + } + } + } + } +} diff --git a/src/main/webapp/app/account/password/password.component.html b/src/main/webapp/app/account/password/password.component.html new file mode 100644 index 00000000..6584df72 --- /dev/null +++ b/src/main/webapp/app/account/password/password.component.html @@ -0,0 +1,152 @@ +
+
+
+

+ Password for [{{ account.login }}] +

+ +
+ Password changed! +
+ +
+ An error has occurred! The password could not be changed. +
+ +
+ The password and its confirmation do not match! +
+ +
+
+ + + +
+ + Your password is required. + +
+
+ +
+ + + +
+ + Your password is required. + + + + Your password is required to be at least 4 characters. + + + + Your password cannot be longer than 50 characters. + +
+ + +
+ +
+ + + +
+ + Your confirmation password is required. + + + + Your confirmation password is required to be at least 4 characters. + + + + Your confirmation password cannot be longer than 50 characters. + +
+
+ + +
+
+
+
diff --git a/src/main/webapp/app/account/password/password.component.spec.ts b/src/main/webapp/app/account/password/password.component.spec.ts new file mode 100644 index 00000000..7ad52b4a --- /dev/null +++ b/src/main/webapp/app/account/password/password.component.spec.ts @@ -0,0 +1,104 @@ +jest.mock('app/core/auth/account.service'); + +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { HttpResponse } from '@angular/common/http'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { FormBuilder } from '@angular/forms'; +import { of, throwError } from 'rxjs'; + +import { AccountService } from 'app/core/auth/account.service'; + +import { PasswordComponent } from './password.component'; +import { PasswordService } from './password.service'; + +describe('PasswordComponent', () => { + let comp: PasswordComponent; + let fixture: ComponentFixture; + let service: PasswordService; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + declarations: [PasswordComponent], + providers: [FormBuilder, AccountService], + }) + .overrideTemplate(PasswordComponent, '') + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(PasswordComponent); + comp = fixture.componentInstance; + service = TestBed.inject(PasswordService); + }); + + it('should show error if passwords do not match', () => { + // GIVEN + comp.passwordForm.patchValue({ + newPassword: 'password1', + confirmPassword: 'password2', + }); + // WHEN + comp.changePassword(); + // THEN + expect(comp.doNotMatch).toBe(true); + expect(comp.error).toBe(false); + expect(comp.success).toBe(false); + }); + + it('should call Auth.changePassword when passwords match', () => { + // GIVEN + const passwordValues = { + currentPassword: 'oldPassword', + newPassword: 'myPassword', + }; + + jest.spyOn(service, 'save').mockReturnValue(of(new HttpResponse({ body: true }))); + + comp.passwordForm.patchValue({ + currentPassword: passwordValues.currentPassword, + newPassword: passwordValues.newPassword, + confirmPassword: passwordValues.newPassword, + }); + + // WHEN + comp.changePassword(); + + // THEN + expect(service.save).toHaveBeenCalledWith(passwordValues.newPassword, passwordValues.currentPassword); + }); + + it('should set success to true upon success', () => { + // GIVEN + jest.spyOn(service, 'save').mockReturnValue(of(new HttpResponse({ body: true }))); + comp.passwordForm.patchValue({ + newPassword: 'myPassword', + confirmPassword: 'myPassword', + }); + + // WHEN + comp.changePassword(); + + // THEN + expect(comp.doNotMatch).toBe(false); + expect(comp.error).toBe(false); + expect(comp.success).toBe(true); + }); + + it('should notify of error if change password fails', () => { + // GIVEN + jest.spyOn(service, 'save').mockReturnValue(throwError('ERROR')); + comp.passwordForm.patchValue({ + newPassword: 'myPassword', + confirmPassword: 'myPassword', + }); + + // WHEN + comp.changePassword(); + + // THEN + expect(comp.doNotMatch).toBe(false); + expect(comp.success).toBe(false); + expect(comp.error).toBe(true); + }); +}); diff --git a/src/main/webapp/app/account/password/password.component.ts b/src/main/webapp/app/account/password/password.component.ts new file mode 100644 index 00000000..bc909b28 --- /dev/null +++ b/src/main/webapp/app/account/password/password.component.ts @@ -0,0 +1,51 @@ +import { Component, OnInit } from '@angular/core'; +import { FormGroup, FormControl, Validators } from '@angular/forms'; +import { Observable } from 'rxjs'; + +import { AccountService } from 'app/core/auth/account.service'; +import { Account } from 'app/core/auth/account.model'; +import { PasswordService } from './password.service'; + +@Component({ + selector: 'jhi-password', + templateUrl: './password.component.html', +}) +export class PasswordComponent implements OnInit { + doNotMatch = false; + error = false; + success = false; + account$?: Observable; + passwordForm = new FormGroup({ + currentPassword: new FormControl('', { nonNullable: true, validators: Validators.required }), + newPassword: new FormControl('', { + nonNullable: true, + validators: [Validators.required, Validators.minLength(4), Validators.maxLength(50)], + }), + confirmPassword: new FormControl('', { + nonNullable: true, + validators: [Validators.required, Validators.minLength(4), Validators.maxLength(50)], + }), + }); + + constructor(private passwordService: PasswordService, private accountService: AccountService) {} + + ngOnInit(): void { + this.account$ = this.accountService.identity(); + } + + changePassword(): void { + this.error = false; + this.success = false; + this.doNotMatch = false; + + const { newPassword, confirmPassword, currentPassword } = this.passwordForm.getRawValue(); + if (newPassword !== confirmPassword) { + this.doNotMatch = true; + } else { + this.passwordService.save(newPassword, currentPassword).subscribe({ + next: () => (this.success = true), + error: () => (this.error = true), + }); + } + } +} diff --git a/src/main/webapp/app/account/password/password.route.ts b/src/main/webapp/app/account/password/password.route.ts new file mode 100644 index 00000000..174b54f5 --- /dev/null +++ b/src/main/webapp/app/account/password/password.route.ts @@ -0,0 +1,13 @@ +import { Route } from '@angular/router'; + +import { UserRouteAccessService } from 'app/core/auth/user-route-access.service'; +import { PasswordComponent } from './password.component'; + +export const passwordRoute: Route = { + path: 'password', + component: PasswordComponent, + data: { + pageTitle: 'global.menu.account.password', + }, + canActivate: [UserRouteAccessService], +}; diff --git a/src/main/webapp/app/account/password/password.service.spec.ts b/src/main/webapp/app/account/password/password.service.spec.ts new file mode 100644 index 00000000..e0d1536f --- /dev/null +++ b/src/main/webapp/app/account/password/password.service.spec.ts @@ -0,0 +1,44 @@ +import { TestBed } from '@angular/core/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; + +import { PasswordService } from './password.service'; +import { ApplicationConfigService } from 'app/core/config/application-config.service'; + +describe('Password Service', () => { + let service: PasswordService; + let httpMock: HttpTestingController; + let applicationConfigService: ApplicationConfigService; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + }); + + service = TestBed.inject(PasswordService); + applicationConfigService = TestBed.inject(ApplicationConfigService); + httpMock = TestBed.inject(HttpTestingController); + }); + + afterEach(() => { + httpMock.verify(); + }); + + describe('Service methods', () => { + it('should call change-password endpoint with correct values', () => { + // GIVEN + const password1 = 'password1'; + const password2 = 'password2'; + + // WHEN + service.save(password2, password1).subscribe(); + + const testRequest = httpMock.expectOne({ + method: 'POST', + url: applicationConfigService.getEndpointFor('api/account/change-password'), + }); + + // THEN + expect(testRequest.request.body).toEqual({ currentPassword: password1, newPassword: password2 }); + }); + }); +}); diff --git a/src/main/webapp/app/account/password/password.service.ts b/src/main/webapp/app/account/password/password.service.ts new file mode 100644 index 00000000..337a64fb --- /dev/null +++ b/src/main/webapp/app/account/password/password.service.ts @@ -0,0 +1,14 @@ +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { Observable } from 'rxjs'; + +import { ApplicationConfigService } from 'app/core/config/application-config.service'; + +@Injectable({ providedIn: 'root' }) +export class PasswordService { + constructor(private http: HttpClient, private applicationConfigService: ApplicationConfigService) {} + + save(newPassword: string, currentPassword: string): Observable<{}> { + return this.http.post(this.applicationConfigService.getEndpointFor('api/account/change-password'), { currentPassword, newPassword }); + } +} diff --git a/src/main/webapp/app/account/register/register.component.html b/src/main/webapp/app/account/register/register.component.html new file mode 100644 index 00000000..c9eb2af9 --- /dev/null +++ b/src/main/webapp/app/account/register/register.component.html @@ -0,0 +1,234 @@ +
+
+
+

Registration

+ +
+ Registration saved! Please check your email for confirmation. +
+ +
+ Registration failed! Please try again later. +
+ +
+ Login name already registered! Please choose another one. +
+ +
+ Email is already in use! Please choose another one. +
+ +
+ The password and its confirmation do not match! +
+
+
+ +
+
+
+
+ + + +
+ + Your username is required. + + + + Your username is required to be at least 1 character. + + + + Your username cannot be longer than 50 characters. + + + + Your username is invalid. + +
+
+ +
+ + + +
+ + Your email is required. + + + + Your email is invalid. + + + + Your email is required to be at least 5 characters. + + + + Your email cannot be longer than 50 characters. + +
+
+ +
+ + + +
+ + Your password is required. + + + + Your password is required to be at least 4 characters. + + + + Your password cannot be longer than 50 characters. + +
+ + +
+ +
+ + + +
+ + Your confirmation password is required. + + + + Your confirmation password is required to be at least 4 characters. + + + + Your confirmation password cannot be longer than 50 characters. + +
+
+ + +
+ +
+ If you want to + sign in, you can try the default accounts:
- Administrator (login="admin" and password="admin")
- User (login="user" and + password="user").
+
+
+
+
diff --git a/src/main/webapp/app/account/register/register.component.spec.ts b/src/main/webapp/app/account/register/register.component.spec.ts new file mode 100644 index 00000000..87019454 --- /dev/null +++ b/src/main/webapp/app/account/register/register.component.spec.ts @@ -0,0 +1,135 @@ +import { ComponentFixture, TestBed, waitForAsync, inject, tick, fakeAsync } from '@angular/core/testing'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { FormBuilder } from '@angular/forms'; +import { of, throwError } from 'rxjs'; +import { TranslateModule, TranslateService } from '@ngx-translate/core'; + +import { EMAIL_ALREADY_USED_TYPE, LOGIN_ALREADY_USED_TYPE } from 'app/config/error.constants'; + +import { RegisterService } from './register.service'; +import { RegisterComponent } from './register.component'; + +describe('RegisterComponent', () => { + let fixture: ComponentFixture; + let comp: RegisterComponent; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot(), HttpClientTestingModule], + declarations: [RegisterComponent], + providers: [FormBuilder], + }) + .overrideTemplate(RegisterComponent, '') + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(RegisterComponent); + comp = fixture.componentInstance; + }); + + it('should ensure the two passwords entered match', () => { + comp.registerForm.patchValue({ + password: 'password', + confirmPassword: 'non-matching', + }); + + comp.register(); + + expect(comp.doNotMatch).toBe(true); + }); + + it('should update success to true after creating an account', inject( + [RegisterService, TranslateService], + fakeAsync((service: RegisterService, mockTranslateService: TranslateService) => { + jest.spyOn(service, 'save').mockReturnValue(of({})); + mockTranslateService.currentLang = 'en'; + comp.registerForm.patchValue({ + password: 'password', + confirmPassword: 'password', + }); + + comp.register(); + tick(); + + expect(service.save).toHaveBeenCalledWith({ + email: '', + password: 'password', + login: '', + langKey: 'en', + }); + expect(comp.success).toBe(true); + expect(comp.errorUserExists).toBe(false); + expect(comp.errorEmailExists).toBe(false); + expect(comp.error).toBe(false); + }) + )); + + it('should notify of user existence upon 400/login already in use', inject( + [RegisterService], + fakeAsync((service: RegisterService) => { + jest.spyOn(service, 'save').mockReturnValue( + throwError({ + status: 400, + error: { type: LOGIN_ALREADY_USED_TYPE }, + }) + ); + comp.registerForm.patchValue({ + password: 'password', + confirmPassword: 'password', + }); + + comp.register(); + tick(); + + expect(comp.errorUserExists).toBe(true); + expect(comp.errorEmailExists).toBe(false); + expect(comp.error).toBe(false); + }) + )); + + it('should notify of email existence upon 400/email address already in use', inject( + [RegisterService], + fakeAsync((service: RegisterService) => { + jest.spyOn(service, 'save').mockReturnValue( + throwError({ + status: 400, + error: { type: EMAIL_ALREADY_USED_TYPE }, + }) + ); + comp.registerForm.patchValue({ + password: 'password', + confirmPassword: 'password', + }); + + comp.register(); + tick(); + + expect(comp.errorEmailExists).toBe(true); + expect(comp.errorUserExists).toBe(false); + expect(comp.error).toBe(false); + }) + )); + + it('should notify of generic error', inject( + [RegisterService], + fakeAsync((service: RegisterService) => { + jest.spyOn(service, 'save').mockReturnValue( + throwError({ + status: 503, + }) + ); + comp.registerForm.patchValue({ + password: 'password', + confirmPassword: 'password', + }); + + comp.register(); + tick(); + + expect(comp.errorUserExists).toBe(false); + expect(comp.errorEmailExists).toBe(false); + expect(comp.error).toBe(true); + }) + )); +}); diff --git a/src/main/webapp/app/account/register/register.component.ts b/src/main/webapp/app/account/register/register.component.ts new file mode 100644 index 00000000..447e95ed --- /dev/null +++ b/src/main/webapp/app/account/register/register.component.ts @@ -0,0 +1,81 @@ +import { Component, AfterViewInit, ElementRef, ViewChild } from '@angular/core'; +import { HttpErrorResponse } from '@angular/common/http'; +import { FormGroup, FormControl, Validators } from '@angular/forms'; +import { TranslateService } from '@ngx-translate/core'; + +import { EMAIL_ALREADY_USED_TYPE, LOGIN_ALREADY_USED_TYPE } from 'app/config/error.constants'; +import { RegisterService } from './register.service'; + +@Component({ + selector: 'jhi-register', + templateUrl: './register.component.html', +}) +export class RegisterComponent implements AfterViewInit { + @ViewChild('login', { static: false }) + login?: ElementRef; + + doNotMatch = false; + error = false; + errorEmailExists = false; + errorUserExists = false; + success = false; + + registerForm = new FormGroup({ + login: new FormControl('', { + nonNullable: true, + validators: [ + Validators.required, + Validators.minLength(1), + Validators.maxLength(50), + Validators.pattern('^[a-zA-Z0-9!$&*+=?^_`{|}~.-]+@[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*$|^[_.@A-Za-z0-9-]+$'), + ], + }), + email: new FormControl('', { + nonNullable: true, + validators: [Validators.required, Validators.minLength(5), Validators.maxLength(254), Validators.email], + }), + password: new FormControl('', { + nonNullable: true, + validators: [Validators.required, Validators.minLength(4), Validators.maxLength(50)], + }), + confirmPassword: new FormControl('', { + nonNullable: true, + validators: [Validators.required, Validators.minLength(4), Validators.maxLength(50)], + }), + }); + + constructor(private translateService: TranslateService, private registerService: RegisterService) {} + + ngAfterViewInit(): void { + if (this.login) { + this.login.nativeElement.focus(); + } + } + + register(): void { + this.doNotMatch = false; + this.error = false; + this.errorEmailExists = false; + this.errorUserExists = false; + + const { password, confirmPassword } = this.registerForm.getRawValue(); + if (password !== confirmPassword) { + this.doNotMatch = true; + } else { + const { login, email } = this.registerForm.getRawValue(); + this.registerService + .save({ login, email, password, langKey: this.translateService.currentLang }) + .subscribe({ next: () => (this.success = true), error: response => this.processError(response) }); + } + } + + private processError(response: HttpErrorResponse): void { + if (response.status === 400 && response.error.type === LOGIN_ALREADY_USED_TYPE) { + this.errorUserExists = true; + } else if (response.status === 400 && response.error.type === EMAIL_ALREADY_USED_TYPE) { + this.errorEmailExists = true; + } else { + this.error = true; + } + } +} diff --git a/src/main/webapp/app/account/register/register.model.ts b/src/main/webapp/app/account/register/register.model.ts new file mode 100644 index 00000000..17508b0d --- /dev/null +++ b/src/main/webapp/app/account/register/register.model.ts @@ -0,0 +1,3 @@ +export class Registration { + constructor(public login: string, public email: string, public password: string, public langKey: string) {} +} diff --git a/src/main/webapp/app/account/register/register.route.ts b/src/main/webapp/app/account/register/register.route.ts new file mode 100644 index 00000000..52525301 --- /dev/null +++ b/src/main/webapp/app/account/register/register.route.ts @@ -0,0 +1,11 @@ +import { Route } from '@angular/router'; + +import { RegisterComponent } from './register.component'; + +export const registerRoute: Route = { + path: 'register', + component: RegisterComponent, + data: { + pageTitle: 'register.title', + }, +}; diff --git a/src/main/webapp/app/account/register/register.service.spec.ts b/src/main/webapp/app/account/register/register.service.spec.ts new file mode 100644 index 00000000..e0d83baf --- /dev/null +++ b/src/main/webapp/app/account/register/register.service.spec.ts @@ -0,0 +1,48 @@ +import { TestBed } from '@angular/core/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; + +import { RegisterService } from './register.service'; +import { ApplicationConfigService } from 'app/core/config/application-config.service'; +import { Registration } from './register.model'; + +describe('RegisterService Service', () => { + let service: RegisterService; + let httpMock: HttpTestingController; + let applicationConfigService: ApplicationConfigService; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + }); + + service = TestBed.inject(RegisterService); + applicationConfigService = TestBed.inject(ApplicationConfigService); + httpMock = TestBed.inject(HttpTestingController); + }); + + afterEach(() => { + httpMock.verify(); + }); + + describe('Service methods', () => { + it('should call register endpoint with correct values', () => { + // GIVEN + const login = 'abc'; + const email = 'test@test.com'; + const password = 'pass'; + const langKey = 'FR'; + const registration = new Registration(login, email, password, langKey); + + // WHEN + service.save(registration).subscribe(); + + const testRequest = httpMock.expectOne({ + method: 'POST', + url: applicationConfigService.getEndpointFor('api/register'), + }); + + // THEN + expect(testRequest.request.body).toEqual({ email, langKey, login, password }); + }); + }); +}); diff --git a/src/main/webapp/app/account/register/register.service.ts b/src/main/webapp/app/account/register/register.service.ts new file mode 100644 index 00000000..8696564e --- /dev/null +++ b/src/main/webapp/app/account/register/register.service.ts @@ -0,0 +1,15 @@ +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { Observable } from 'rxjs'; + +import { ApplicationConfigService } from 'app/core/config/application-config.service'; +import { Registration } from './register.model'; + +@Injectable({ providedIn: 'root' }) +export class RegisterService { + constructor(private http: HttpClient, private applicationConfigService: ApplicationConfigService) {} + + save(registration: Registration): Observable<{}> { + return this.http.post(this.applicationConfigService.getEndpointFor('api/register'), registration); + } +} diff --git a/src/main/webapp/app/account/settings/settings.component.html b/src/main/webapp/app/account/settings/settings.component.html new file mode 100644 index 00000000..4fb7967e --- /dev/null +++ b/src/main/webapp/app/account/settings/settings.component.html @@ -0,0 +1,166 @@ +
+
+
+

+ User settings for [{{ settingsForm.value.login }}] +

+ +
+ Settings saved! +
+ + + +
+
+ + + +
+ + Your first name is required. + + + + Your first name is required to be at least 1 character + + + + Your first name cannot be longer than 50 characters + +
+
+ +
+ + + +
+ + Your last name is required. + + + + Your last name is required to be at least 1 character + + + + Your last name cannot be longer than 50 characters + +
+
+ +
+ + + +
+ + Your email is required. + + + + Your email is invalid. + + + + Your email is required to be at least 5 characters. + + + + Your email cannot be longer than 50 characters. + +
+
+ +
+ + +
+ + +
+
+
+
diff --git a/src/main/webapp/app/account/settings/settings.component.spec.ts b/src/main/webapp/app/account/settings/settings.component.spec.ts new file mode 100644 index 00000000..0b540754 --- /dev/null +++ b/src/main/webapp/app/account/settings/settings.component.spec.ts @@ -0,0 +1,91 @@ +jest.mock('app/core/auth/account.service'); + +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { FormBuilder } from '@angular/forms'; +import { throwError, of } from 'rxjs'; +import { TranslateModule } from '@ngx-translate/core'; + +import { AccountService } from 'app/core/auth/account.service'; +import { Account } from 'app/core/auth/account.model'; + +import { SettingsComponent } from './settings.component'; + +describe('SettingsComponent', () => { + let comp: SettingsComponent; + let fixture: ComponentFixture; + let mockAccountService: AccountService; + const account: Account = { + firstName: 'John', + lastName: 'Doe', + activated: true, + email: 'john.doe@mail.com', + langKey: 'en', + login: 'john', + authorities: [], + imageUrl: '', + }; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot(), HttpClientTestingModule], + declarations: [SettingsComponent], + providers: [FormBuilder, AccountService], + }) + .overrideTemplate(SettingsComponent, '') + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(SettingsComponent); + comp = fixture.componentInstance; + mockAccountService = TestBed.inject(AccountService); + mockAccountService.identity = jest.fn(() => of(account)); + mockAccountService.getAuthenticationState = jest.fn(() => of(account)); + }); + + it('should send the current identity upon save', () => { + // GIVEN + mockAccountService.save = jest.fn(() => of({})); + const settingsFormValues = { + firstName: 'John', + lastName: 'Doe', + email: 'john.doe@mail.com', + langKey: 'en', + }; + + // WHEN + comp.ngOnInit(); + comp.save(); + + // THEN + expect(mockAccountService.identity).toHaveBeenCalled(); + expect(mockAccountService.save).toHaveBeenCalledWith(account); + expect(mockAccountService.authenticate).toHaveBeenCalledWith(account); + expect(comp.settingsForm.value).toMatchObject(expect.objectContaining(settingsFormValues)); + }); + + it('should notify of success upon successful save', () => { + // GIVEN + mockAccountService.save = jest.fn(() => of({})); + + // WHEN + comp.ngOnInit(); + comp.save(); + + // THEN + expect(comp.success).toBe(true); + }); + + it('should notify of error upon failed save', () => { + // GIVEN + mockAccountService.save = jest.fn(() => throwError('ERROR')); + + // WHEN + comp.ngOnInit(); + comp.save(); + + // THEN + expect(comp.success).toBe(false); + }); +}); diff --git a/src/main/webapp/app/account/settings/settings.component.ts b/src/main/webapp/app/account/settings/settings.component.ts new file mode 100644 index 00000000..0e991711 --- /dev/null +++ b/src/main/webapp/app/account/settings/settings.component.ts @@ -0,0 +1,64 @@ +import { Component, OnInit } from '@angular/core'; +import { FormGroup, FormControl, Validators } from '@angular/forms'; +import { TranslateService } from '@ngx-translate/core'; + +import { AccountService } from 'app/core/auth/account.service'; +import { Account } from 'app/core/auth/account.model'; +import { LANGUAGES } from 'app/config/language.constants'; + +const initialAccount: Account = {} as Account; + +@Component({ + selector: 'jhi-settings', + templateUrl: './settings.component.html', +}) +export class SettingsComponent implements OnInit { + success = false; + languages = LANGUAGES; + + settingsForm = new FormGroup({ + firstName: new FormControl(initialAccount.firstName, { + nonNullable: true, + validators: [Validators.required, Validators.minLength(1), Validators.maxLength(50)], + }), + lastName: new FormControl(initialAccount.lastName, { + nonNullable: true, + validators: [Validators.required, Validators.minLength(1), Validators.maxLength(50)], + }), + email: new FormControl(initialAccount.email, { + nonNullable: true, + validators: [Validators.required, Validators.minLength(5), Validators.maxLength(254), Validators.email], + }), + langKey: new FormControl(initialAccount.langKey, { nonNullable: true }), + + activated: new FormControl(initialAccount.activated, { nonNullable: true }), + authorities: new FormControl(initialAccount.authorities, { nonNullable: true }), + imageUrl: new FormControl(initialAccount.imageUrl, { nonNullable: true }), + login: new FormControl(initialAccount.login, { nonNullable: true }), + }); + + constructor(private accountService: AccountService, private translateService: TranslateService) {} + + ngOnInit(): void { + this.accountService.identity().subscribe(account => { + if (account) { + this.settingsForm.patchValue(account); + } + }); + } + + save(): void { + this.success = false; + + const account = this.settingsForm.getRawValue(); + this.accountService.save(account).subscribe(() => { + this.success = true; + + this.accountService.authenticate(account); + + if (account.langKey !== this.translateService.currentLang) { + this.translateService.use(account.langKey); + } + }); + } +} diff --git a/src/main/webapp/app/account/settings/settings.route.ts b/src/main/webapp/app/account/settings/settings.route.ts new file mode 100644 index 00000000..c940aece --- /dev/null +++ b/src/main/webapp/app/account/settings/settings.route.ts @@ -0,0 +1,13 @@ +import { Route } from '@angular/router'; + +import { UserRouteAccessService } from 'app/core/auth/user-route-access.service'; +import { SettingsComponent } from './settings.component'; + +export const settingsRoute: Route = { + path: 'settings', + component: SettingsComponent, + data: { + pageTitle: 'global.menu.account.settings', + }, + canActivate: [UserRouteAccessService], +}; diff --git a/src/main/webapp/app/admin/admin-routing.module.ts b/src/main/webapp/app/admin/admin-routing.module.ts new file mode 100644 index 00000000..6eec19da --- /dev/null +++ b/src/main/webapp/app/admin/admin-routing.module.ts @@ -0,0 +1,47 @@ +import { NgModule } from '@angular/core'; +import { RouterModule } from '@angular/router'; +import { TwentyOnePointsElasticsearchReindexModule } from './elasticsearch-reindex/elasticsearch-reindex.module'; +/* jhipster-needle-add-admin-module-import - JHipster will add admin modules imports here */ + +@NgModule({ + imports: [ + TwentyOnePointsElasticsearchReindexModule, + /* jhipster-needle-add-admin-module - JHipster will add admin modules here */ + RouterModule.forChild([ + { + path: 'user-management', + loadChildren: () => import('./user-management/user-management.module').then(m => m.UserManagementModule), + data: { + pageTitle: 'userManagement.home.title', + }, + }, + { + path: 'docs', + loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule), + }, + { + path: 'configuration', + loadChildren: () => import('./configuration/configuration.module').then(m => m.ConfigurationModule), + }, + { + path: 'health', + loadChildren: () => import('./health/health.module').then(m => m.HealthModule), + }, + { + path: 'logs', + loadChildren: () => import('./logs/logs.module').then(m => m.LogsModule), + }, + { + path: 'metrics', + loadChildren: () => import('./metrics/metrics.module').then(m => m.MetricsModule), + }, + { + path: 'elasticsearch-reindex', + loadChildren: () => + import('./elasticsearch-reindex/elasticsearch-reindex.module').then(m => m.TwentyOnePointsElasticsearchReindexModule), + }, + /* jhipster-needle-add-admin-route - JHipster will add admin routes here */ + ]), + ], +}) +export class AdminRoutingModule {} diff --git a/src/main/webapp/app/admin/configuration/configuration.component.html b/src/main/webapp/app/admin/configuration/configuration.component.html new file mode 100644 index 00000000..e76d02c3 --- /dev/null +++ b/src/main/webapp/app/admin/configuration/configuration.component.html @@ -0,0 +1,57 @@ +
+

Configuration

+ + Filter (by prefix) + + +

Spring configuration

+ + + + + + + + + + + + + + +
+ Prefix + Properties
+ {{ bean.prefix }} + +
+
{{ property.key }}
+
+ {{ property.value | json }} +
+
+
+ +
+

+ {{ propertySource.name }} +

+ + + + + + + + + + + + + + +
PropertyValue
{{ property.key }} + {{ property.value.value }} +
+
+
diff --git a/src/main/webapp/app/admin/configuration/configuration.component.spec.ts b/src/main/webapp/app/admin/configuration/configuration.component.spec.ts new file mode 100644 index 00000000..12df2b28 --- /dev/null +++ b/src/main/webapp/app/admin/configuration/configuration.component.spec.ts @@ -0,0 +1,67 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { of } from 'rxjs'; + +import { ConfigurationComponent } from './configuration.component'; +import { ConfigurationService } from './configuration.service'; +import { Bean, PropertySource } from './configuration.model'; + +describe('ConfigurationComponent', () => { + let comp: ConfigurationComponent; + let fixture: ComponentFixture; + let service: ConfigurationService; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + declarations: [ConfigurationComponent], + providers: [ConfigurationService], + }) + .overrideTemplate(ConfigurationComponent, '') + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ConfigurationComponent); + comp = fixture.componentInstance; + service = TestBed.inject(ConfigurationService); + }); + + describe('OnInit', () => { + it('Should call load all on init', () => { + // GIVEN + const beans: Bean[] = [ + { + prefix: 'jhipster', + properties: { + clientApp: { + name: 'jhipsterApp', + }, + }, + }, + ]; + const propertySources: PropertySource[] = [ + { + name: 'server.ports', + properties: { + 'local.server.port': { + value: '8080', + }, + }, + }, + ]; + jest.spyOn(service, 'getBeans').mockReturnValue(of(beans)); + jest.spyOn(service, 'getPropertySources').mockReturnValue(of(propertySources)); + + // WHEN + comp.ngOnInit(); + + // THEN + expect(service.getBeans).toHaveBeenCalled(); + expect(service.getPropertySources).toHaveBeenCalled(); + expect(comp.allBeans).toEqual(beans); + expect(comp.beans).toEqual(beans); + expect(comp.propertySources).toEqual(propertySources); + }); + }); +}); diff --git a/src/main/webapp/app/admin/configuration/configuration.component.ts b/src/main/webapp/app/admin/configuration/configuration.component.ts new file mode 100644 index 00000000..939d1aec --- /dev/null +++ b/src/main/webapp/app/admin/configuration/configuration.component.ts @@ -0,0 +1,35 @@ +import { Component, OnInit } from '@angular/core'; + +import { ConfigurationService } from './configuration.service'; +import { Bean, PropertySource } from './configuration.model'; + +@Component({ + selector: 'jhi-configuration', + templateUrl: './configuration.component.html', +}) +export class ConfigurationComponent implements OnInit { + allBeans!: Bean[]; + beans: Bean[] = []; + beansFilter = ''; + beansAscending = true; + propertySources: PropertySource[] = []; + + constructor(private configurationService: ConfigurationService) {} + + ngOnInit(): void { + this.configurationService.getBeans().subscribe(beans => { + this.allBeans = beans; + this.filterAndSortBeans(); + }); + + this.configurationService.getPropertySources().subscribe(propertySources => (this.propertySources = propertySources)); + } + + filterAndSortBeans(): void { + const beansAscendingValue = this.beansAscending ? -1 : 1; + const beansAscendingValueReverse = this.beansAscending ? 1 : -1; + this.beans = this.allBeans + .filter(bean => !this.beansFilter || bean.prefix.toLowerCase().includes(this.beansFilter.toLowerCase())) + .sort((a, b) => (a.prefix < b.prefix ? beansAscendingValue : beansAscendingValueReverse)); + } +} diff --git a/src/main/webapp/app/admin/configuration/configuration.model.ts b/src/main/webapp/app/admin/configuration/configuration.model.ts new file mode 100644 index 00000000..6a671e0a --- /dev/null +++ b/src/main/webapp/app/admin/configuration/configuration.model.ts @@ -0,0 +1,40 @@ +export interface ConfigProps { + contexts: Contexts; +} + +export interface Contexts { + [key: string]: Context; +} + +export interface Context { + beans: Beans; + parentId?: any; +} + +export interface Beans { + [key: string]: Bean; +} + +export interface Bean { + prefix: string; + properties: any; +} + +export interface Env { + activeProfiles?: string[]; + propertySources: PropertySource[]; +} + +export interface PropertySource { + name: string; + properties: Properties; +} + +export interface Properties { + [key: string]: Property; +} + +export interface Property { + value: string; + origin?: string; +} diff --git a/src/main/webapp/app/admin/configuration/configuration.module.ts b/src/main/webapp/app/admin/configuration/configuration.module.ts new file mode 100644 index 00000000..43ade13c --- /dev/null +++ b/src/main/webapp/app/admin/configuration/configuration.module.ts @@ -0,0 +1,12 @@ +import { NgModule } from '@angular/core'; +import { RouterModule } from '@angular/router'; +import { SharedModule } from 'app/shared/shared.module'; + +import { ConfigurationComponent } from './configuration.component'; +import { configurationRoute } from './configuration.route'; + +@NgModule({ + imports: [SharedModule, RouterModule.forChild([configurationRoute])], + declarations: [ConfigurationComponent], +}) +export class ConfigurationModule {} diff --git a/src/main/webapp/app/admin/configuration/configuration.route.ts b/src/main/webapp/app/admin/configuration/configuration.route.ts new file mode 100644 index 00000000..119e4335 --- /dev/null +++ b/src/main/webapp/app/admin/configuration/configuration.route.ts @@ -0,0 +1,11 @@ +import { Route } from '@angular/router'; + +import { ConfigurationComponent } from './configuration.component'; + +export const configurationRoute: Route = { + path: '', + component: ConfigurationComponent, + data: { + pageTitle: 'configuration.title', + }, +}; diff --git a/src/main/webapp/app/admin/configuration/configuration.service.spec.ts b/src/main/webapp/app/admin/configuration/configuration.service.spec.ts new file mode 100644 index 00000000..6e6ff7f4 --- /dev/null +++ b/src/main/webapp/app/admin/configuration/configuration.service.spec.ts @@ -0,0 +1,71 @@ +import { TestBed } from '@angular/core/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; + +import { ConfigurationService } from './configuration.service'; +import { Bean, ConfigProps, Env, PropertySource } from './configuration.model'; + +describe('Logs Service', () => { + let service: ConfigurationService; + let httpMock: HttpTestingController; + let expectedResult: Bean[] | PropertySource[] | null; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + }); + + expectedResult = null; + service = TestBed.inject(ConfigurationService); + httpMock = TestBed.inject(HttpTestingController); + }); + + afterEach(() => { + httpMock.verify(); + }); + + describe('Service methods', () => { + it('should get the config', () => { + const bean: Bean = { + prefix: 'jhipster', + properties: { + clientApp: { + name: 'jhipsterApp', + }, + }, + }; + const configProps: ConfigProps = { + contexts: { + jhipster: { + beans: { + 'tech.jhipster.config.JHipsterProperties': bean, + }, + }, + }, + }; + service.getBeans().subscribe(received => (expectedResult = received)); + + const req = httpMock.expectOne({ method: 'GET' }); + req.flush(configProps); + expect(expectedResult).toEqual([bean]); + }); + + it('should get the env', () => { + const propertySources: PropertySource[] = [ + { + name: 'server.ports', + properties: { + 'local.server.port': { + value: '8080', + }, + }, + }, + ]; + const env: Env = { propertySources }; + service.getPropertySources().subscribe(received => (expectedResult = received)); + + const req = httpMock.expectOne({ method: 'GET' }); + req.flush(env); + expect(expectedResult).toEqual(propertySources); + }); + }); +}); diff --git a/src/main/webapp/app/admin/configuration/configuration.service.ts b/src/main/webapp/app/admin/configuration/configuration.service.ts new file mode 100644 index 00000000..d8d30518 --- /dev/null +++ b/src/main/webapp/app/admin/configuration/configuration.service.ts @@ -0,0 +1,28 @@ +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; + +import { ApplicationConfigService } from 'app/core/config/application-config.service'; +import { Bean, Beans, ConfigProps, Env, PropertySource } from './configuration.model'; + +@Injectable({ providedIn: 'root' }) +export class ConfigurationService { + constructor(private http: HttpClient, private applicationConfigService: ApplicationConfigService) {} + + getBeans(): Observable { + return this.http.get(this.applicationConfigService.getEndpointFor('management/configprops')).pipe( + map(configProps => + Object.values( + Object.values(configProps.contexts) + .map(context => context.beans) + .reduce((allBeans: Beans, contextBeans: Beans) => ({ ...allBeans, ...contextBeans })) + ) + ) + ); + } + + getPropertySources(): Observable { + return this.http.get(this.applicationConfigService.getEndpointFor('management/env')).pipe(map(env => env.propertySources)); + } +} diff --git a/src/main/webapp/app/admin/docs/docs.component.html b/src/main/webapp/app/admin/docs/docs.component.html new file mode 100644 index 00000000..24025522 --- /dev/null +++ b/src/main/webapp/app/admin/docs/docs.component.html @@ -0,0 +1,10 @@ + diff --git a/src/main/webapp/app/admin/docs/docs.component.scss b/src/main/webapp/app/admin/docs/docs.component.scss new file mode 100644 index 00000000..541c3d79 --- /dev/null +++ b/src/main/webapp/app/admin/docs/docs.component.scss @@ -0,0 +1,6 @@ +@import '~bootstrap/scss/functions'; +@import '~bootstrap/scss/variables'; + +iframe { + background: white; +} diff --git a/src/main/webapp/app/admin/docs/docs.component.ts b/src/main/webapp/app/admin/docs/docs.component.ts new file mode 100644 index 00000000..53b4b14c --- /dev/null +++ b/src/main/webapp/app/admin/docs/docs.component.ts @@ -0,0 +1,8 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'jhi-docs', + templateUrl: './docs.component.html', + styleUrls: ['./docs.component.scss'], +}) +export class DocsComponent {} diff --git a/src/main/webapp/app/admin/docs/docs.module.ts b/src/main/webapp/app/admin/docs/docs.module.ts new file mode 100644 index 00000000..7b9bbb66 --- /dev/null +++ b/src/main/webapp/app/admin/docs/docs.module.ts @@ -0,0 +1,12 @@ +import { NgModule } from '@angular/core'; +import { RouterModule } from '@angular/router'; +import { SharedModule } from 'app/shared/shared.module'; + +import { DocsComponent } from './docs.component'; +import { docsRoute } from './docs.route'; + +@NgModule({ + imports: [SharedModule, RouterModule.forChild([docsRoute])], + declarations: [DocsComponent], +}) +export class DocsModule {} diff --git a/src/main/webapp/app/admin/docs/docs.route.ts b/src/main/webapp/app/admin/docs/docs.route.ts new file mode 100644 index 00000000..e51770df --- /dev/null +++ b/src/main/webapp/app/admin/docs/docs.route.ts @@ -0,0 +1,11 @@ +import { Route } from '@angular/router'; + +import { DocsComponent } from './docs.component'; + +export const docsRoute: Route = { + path: '', + component: DocsComponent, + data: { + pageTitle: 'global.menu.admin.apidocs', + }, +}; diff --git a/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex-modal.component.html b/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex-modal.component.html new file mode 100644 index 00000000..220e35eb --- /dev/null +++ b/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex-modal.component.html @@ -0,0 +1,18 @@ +
+ + + +
diff --git a/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex-modal.component.ts b/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex-modal.component.ts new file mode 100644 index 00000000..9996b5d9 --- /dev/null +++ b/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex-modal.component.ts @@ -0,0 +1,16 @@ +import { Component } from '@angular/core'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; + +import { ElasticsearchReindexService } from './elasticsearch-reindex.service'; + +@Component({ + selector: 'jhi-elasticsearch-reindex-modal', + templateUrl: './elasticsearch-reindex-modal.component.html', +}) +export class ElasticsearchReindexModalComponent { + constructor(private elasticsearchReindexService: ElasticsearchReindexService, public activeModal: NgbActiveModal) {} + + reindex(): void { + this.elasticsearchReindexService.reindex().subscribe(() => this.activeModal.dismiss()); + } +} diff --git a/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex-selected-modal.component.ts b/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex-selected-modal.component.ts new file mode 100644 index 00000000..38649743 --- /dev/null +++ b/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex-selected-modal.component.ts @@ -0,0 +1,19 @@ +import { Component } from '@angular/core'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; + +import { ElasticsearchReindexService } from './elasticsearch-reindex.service'; + +@Component({ + selector: 'jhi-elasticsearch-reindex-modal', + templateUrl: './elasticsearch-reindex-modal.component.html', +}) +export class ElasticsearchReindexSelectedModalComponent { + entities: string[]; + constructor(private elasticsearchReindexService: ElasticsearchReindexService, public activeModal: NgbActiveModal) { + this.entities = []; + } + + reindex(): void { + this.elasticsearchReindexService.reindexSelected(this.entities).subscribe(() => this.activeModal.dismiss()); + } +} diff --git a/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex.component.html b/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex.component.html new file mode 100644 index 00000000..57608da0 --- /dev/null +++ b/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex.component.html @@ -0,0 +1,31 @@ +
+

+ Reindex Elasticsearch +

+ +

+ This will send a request to reindex all data from your primary datasource into Elasticsearch. This will fix missing data and mapping + issues, however you will lose any ES data and mapping that was defined outside of your application. This request will return immediately + and the reindexing will run in the background. You will see "Elasticsearch: Successfully performed reindexing" in your logs when this is + complete. +

+

+ This can take a long time and is susceptible to concurrency issues so it should be performed when the number of users is low. +

+ + +
+ +
+
+
+ +
diff --git a/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex.component.ts b/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex.component.ts new file mode 100644 index 00000000..1e64bc2a --- /dev/null +++ b/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex.component.ts @@ -0,0 +1,41 @@ +import { Component } from '@angular/core'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; + +import { ElasticsearchReindexModalComponent } from './elasticsearch-reindex-modal.component'; +import { ElasticsearchReindexSelectedModalComponent } from './elasticsearch-reindex-selected-modal.component'; + +@Component({ + selector: 'jhi-elasticsearch-reindex', + templateUrl: './elasticsearch-reindex.component.html', +}) +export class ElasticsearchReindexComponent { + entities: string[]; + reindexType: string; + checks: { [key: string]: boolean } = {}; + + constructor(private modalService: NgbModal) { + this.reindexType = 'all'; + this.entities = ['Points', 'Weight', 'BloodPressure', 'Preferences', 'User']; + } + + doReindex(): void { + if (this.reindexType === 'all') { + this.showConfirm(); + } else { + this.showConfirmSelected(); + } + } + + showConfirm(): void { + this.modalService.open(ElasticsearchReindexModalComponent); + } + + showConfirmSelected(): void { + const activeModal = this.modalService.open(ElasticsearchReindexSelectedModalComponent); + const checks = this.checks; + const reindexList = this.entities.filter(function (name): any { + return checks[name]; + }); + activeModal.componentInstance.entities = reindexList; + } +} diff --git a/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex.module.ts b/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex.module.ts new file mode 100644 index 00000000..fbb945cf --- /dev/null +++ b/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex.module.ts @@ -0,0 +1,23 @@ +import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; +import { RouterModule } from '@angular/router'; + +import { SharedModule } from 'app/shared/shared.module'; + +import { + ElasticsearchReindexComponent, + ElasticsearchReindexModalComponent, + ElasticsearchReindexSelectedModalComponent, + ElasticsearchReindexService, + elasticsearchReindexRoute, +} from './'; + +const ADMIN_ROUTES = [elasticsearchReindexRoute]; + +@NgModule({ + imports: [SharedModule, RouterModule.forChild(ADMIN_ROUTES)], + declarations: [ElasticsearchReindexComponent, ElasticsearchReindexModalComponent, ElasticsearchReindexSelectedModalComponent], + entryComponents: [ElasticsearchReindexModalComponent, ElasticsearchReindexSelectedModalComponent], + providers: [ElasticsearchReindexService], + schemas: [CUSTOM_ELEMENTS_SCHEMA], +}) +export class TwentyOnePointsElasticsearchReindexModule {} diff --git a/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex.route.ts b/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex.route.ts new file mode 100644 index 00000000..04924b9c --- /dev/null +++ b/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex.route.ts @@ -0,0 +1,13 @@ +import { Route } from '@angular/router'; +import { UserRouteAccessService } from 'app/core/auth/user-route-access.service'; +import { ElasticsearchReindexComponent } from './elasticsearch-reindex.component'; + +export const elasticsearchReindexRoute: Route = { + path: '', + component: ElasticsearchReindexComponent, + data: { + authorities: ['ROLE_ADMIN'], + pageTitle: 'elasticsearch.reindex.title', + }, + canActivate: [UserRouteAccessService], +}; diff --git a/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex.service.ts b/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex.service.ts new file mode 100644 index 00000000..26fc1c65 --- /dev/null +++ b/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex.service.ts @@ -0,0 +1,16 @@ +import { Injectable } from '@angular/core'; +import { HttpClient, HttpResponse } from '@angular/common/http'; +import { Observable } from 'rxjs'; + +@Injectable({ providedIn: 'root' }) +export class ElasticsearchReindexService { + constructor(private http: HttpClient) {} + + reindex(): Observable> { + return this.http.post>('api/elasticsearch/index', { observe: 'response' }); + } + + reindexSelected(selectedEntities: string[]): Observable> { + return this.http.post>('api/elasticsearch/selected', selectedEntities, { observe: 'response' }); + } +} diff --git a/src/main/webapp/app/admin/elasticsearch-reindex/index.ts b/src/main/webapp/app/admin/elasticsearch-reindex/index.ts new file mode 100644 index 00000000..d90a6576 --- /dev/null +++ b/src/main/webapp/app/admin/elasticsearch-reindex/index.ts @@ -0,0 +1,5 @@ +export * from './elasticsearch-reindex.component'; +export * from './elasticsearch-reindex-modal.component'; +export * from './elasticsearch-reindex-selected-modal.component'; +export * from './elasticsearch-reindex.service'; +export * from './elasticsearch-reindex.route'; diff --git a/src/main/webapp/app/admin/health/health.component.html b/src/main/webapp/app/admin/health/health.component.html new file mode 100644 index 00000000..08e62ac3 --- /dev/null +++ b/src/main/webapp/app/admin/health/health.component.html @@ -0,0 +1,48 @@ +
+

+ Health Checks + + +

+ +
+ + + + + + + + + + + + + + + +
Service nameStatusDetails
+ {{ 'health.indicator.' + componentHealth.key | translate }} + + + {{ + { UNKNOWN: 'UNKNOWN', UP: 'UP', OUT_OF_SERVICE: 'OUT_OF_SERVICE', DOWN: 'DOWN' }[componentHealth.value!.status || 'UNKNOWN'] + }} + + + + + +
+
+
diff --git a/src/main/webapp/app/admin/health/health.component.spec.ts b/src/main/webapp/app/admin/health/health.component.spec.ts new file mode 100644 index 00000000..3cfdf9f8 --- /dev/null +++ b/src/main/webapp/app/admin/health/health.component.spec.ts @@ -0,0 +1,66 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { HttpErrorResponse } from '@angular/common/http'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { of, throwError } from 'rxjs'; + +import { HealthComponent } from './health.component'; +import { HealthService } from './health.service'; +import { Health } from './health.model'; + +describe('HealthComponent', () => { + let comp: HealthComponent; + let fixture: ComponentFixture; + let service: HealthService; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + declarations: [HealthComponent], + }) + .overrideTemplate(HealthComponent, '') + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(HealthComponent); + comp = fixture.componentInstance; + service = TestBed.inject(HealthService); + }); + + describe('getBadgeClass', () => { + it('should get badge class', () => { + const upBadgeClass = comp.getBadgeClass('UP'); + const downBadgeClass = comp.getBadgeClass('DOWN'); + expect(upBadgeClass).toEqual('bg-success'); + expect(downBadgeClass).toEqual('bg-danger'); + }); + }); + + describe('refresh', () => { + it('should call refresh on init', () => { + // GIVEN + const health: Health = { status: 'UP', components: { mail: { status: 'UP', details: { mailDetail: 'mail' } } } }; + jest.spyOn(service, 'checkHealth').mockReturnValue(of(health)); + + // WHEN + comp.ngOnInit(); + + // THEN + expect(service.checkHealth).toHaveBeenCalled(); + expect(comp.health).toEqual(health); + }); + + it('should handle a 503 on refreshing health data', () => { + // GIVEN + const health: Health = { status: 'DOWN', components: { mail: { status: 'DOWN' } } }; + jest.spyOn(service, 'checkHealth').mockReturnValue(throwError(new HttpErrorResponse({ status: 503, error: health }))); + + // WHEN + comp.refresh(); + + // THEN + expect(service.checkHealth).toHaveBeenCalled(); + expect(comp.health).toEqual(health); + }); + }); +}); diff --git a/src/main/webapp/app/admin/health/health.component.ts b/src/main/webapp/app/admin/health/health.component.ts new file mode 100644 index 00000000..2d9bc2d0 --- /dev/null +++ b/src/main/webapp/app/admin/health/health.component.ts @@ -0,0 +1,44 @@ +import { Component, OnInit } from '@angular/core'; +import { HttpErrorResponse } from '@angular/common/http'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; + +import { HealthService } from './health.service'; +import { Health, HealthDetails, HealthStatus } from './health.model'; +import { HealthModalComponent } from './modal/health-modal.component'; + +@Component({ + selector: 'jhi-health', + templateUrl: './health.component.html', +}) +export class HealthComponent implements OnInit { + health?: Health; + + constructor(private modalService: NgbModal, private healthService: HealthService) {} + + ngOnInit(): void { + this.refresh(); + } + + getBadgeClass(statusState: HealthStatus): string { + if (statusState === 'UP') { + return 'bg-success'; + } + return 'bg-danger'; + } + + refresh(): void { + this.healthService.checkHealth().subscribe({ + next: health => (this.health = health), + error: (error: HttpErrorResponse) => { + if (error.status === 503) { + this.health = error.error; + } + }, + }); + } + + showHealth(health: { key: string; value: HealthDetails }): void { + const modalRef = this.modalService.open(HealthModalComponent); + modalRef.componentInstance.health = health; + } +} diff --git a/src/main/webapp/app/admin/health/health.model.ts b/src/main/webapp/app/admin/health/health.model.ts new file mode 100644 index 00000000..be230363 --- /dev/null +++ b/src/main/webapp/app/admin/health/health.model.ts @@ -0,0 +1,15 @@ +export type HealthStatus = 'UP' | 'DOWN' | 'UNKNOWN' | 'OUT_OF_SERVICE'; + +export type HealthKey = 'diskSpace' | 'mail' | 'ping' | 'livenessState' | 'readinessState' | 'elasticsearch' | 'db'; + +export interface Health { + status: HealthStatus; + components: { + [key in HealthKey]?: HealthDetails; + }; +} + +export interface HealthDetails { + status: HealthStatus; + details?: { [key: string]: unknown }; +} diff --git a/src/main/webapp/app/admin/health/health.module.ts b/src/main/webapp/app/admin/health/health.module.ts new file mode 100644 index 00000000..554d1b8e --- /dev/null +++ b/src/main/webapp/app/admin/health/health.module.ts @@ -0,0 +1,13 @@ +import { NgModule } from '@angular/core'; +import { RouterModule } from '@angular/router'; +import { SharedModule } from 'app/shared/shared.module'; + +import { HealthComponent } from './health.component'; +import { HealthModalComponent } from './modal/health-modal.component'; +import { healthRoute } from './health.route'; + +@NgModule({ + imports: [SharedModule, RouterModule.forChild([healthRoute])], + declarations: [HealthComponent, HealthModalComponent], +}) +export class HealthModule {} diff --git a/src/main/webapp/app/admin/health/health.route.ts b/src/main/webapp/app/admin/health/health.route.ts new file mode 100644 index 00000000..bd760272 --- /dev/null +++ b/src/main/webapp/app/admin/health/health.route.ts @@ -0,0 +1,11 @@ +import { Route } from '@angular/router'; + +import { HealthComponent } from './health.component'; + +export const healthRoute: Route = { + path: '', + component: HealthComponent, + data: { + pageTitle: 'health.title', + }, +}; diff --git a/src/main/webapp/app/admin/health/health.service.spec.ts b/src/main/webapp/app/admin/health/health.service.spec.ts new file mode 100644 index 00000000..850c531f --- /dev/null +++ b/src/main/webapp/app/admin/health/health.service.spec.ts @@ -0,0 +1,48 @@ +import { TestBed } from '@angular/core/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; + +import { HealthService } from './health.service'; +import { ApplicationConfigService } from 'app/core/config/application-config.service'; + +describe('HealthService Service', () => { + let service: HealthService; + let httpMock: HttpTestingController; + let applicationConfigService: ApplicationConfigService; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + }); + + service = TestBed.inject(HealthService); + applicationConfigService = TestBed.inject(ApplicationConfigService); + httpMock = TestBed.inject(HttpTestingController); + }); + + afterEach(() => { + httpMock.verify(); + }); + + describe('Service methods', () => { + it('should call management/health endpoint with correct values', () => { + // GIVEN + let expectedResult; + const checkHealth = { + components: [], + }; + + // WHEN + service.checkHealth().subscribe(received => { + expectedResult = received; + }); + const testRequest = httpMock.expectOne({ + method: 'GET', + url: applicationConfigService.getEndpointFor('management/health'), + }); + testRequest.flush(checkHealth); + + // THEN + expect(expectedResult).toEqual(checkHealth); + }); + }); +}); diff --git a/src/main/webapp/app/admin/health/health.service.ts b/src/main/webapp/app/admin/health/health.service.ts new file mode 100644 index 00000000..4712a978 --- /dev/null +++ b/src/main/webapp/app/admin/health/health.service.ts @@ -0,0 +1,15 @@ +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { Observable } from 'rxjs'; + +import { ApplicationConfigService } from 'app/core/config/application-config.service'; +import { Health } from './health.model'; + +@Injectable({ providedIn: 'root' }) +export class HealthService { + constructor(private http: HttpClient, private applicationConfigService: ApplicationConfigService) {} + + checkHealth(): Observable { + return this.http.get(this.applicationConfigService.getEndpointFor('management/health')); + } +} diff --git a/src/main/webapp/app/admin/health/modal/health-modal.component.html b/src/main/webapp/app/admin/health/modal/health-modal.component.html new file mode 100644 index 00000000..f55b8765 --- /dev/null +++ b/src/main/webapp/app/admin/health/modal/health-modal.component.html @@ -0,0 +1,36 @@ + + + + + diff --git a/src/main/webapp/app/admin/health/modal/health-modal.component.spec.ts b/src/main/webapp/app/admin/health/modal/health-modal.component.spec.ts new file mode 100644 index 00000000..9eb92d0e --- /dev/null +++ b/src/main/webapp/app/admin/health/modal/health-modal.component.spec.ts @@ -0,0 +1,112 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; + +import { HealthModalComponent } from './health-modal.component'; + +describe('HealthModalComponent', () => { + let comp: HealthModalComponent; + let fixture: ComponentFixture; + let mockActiveModal: NgbActiveModal; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + declarations: [HealthModalComponent], + providers: [NgbActiveModal], + }) + .overrideTemplate(HealthModalComponent, '') + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(HealthModalComponent); + comp = fixture.componentInstance; + mockActiveModal = TestBed.inject(NgbActiveModal); + }); + + describe('readableValue', () => { + it('should return stringify value', () => { + // GIVEN + comp.health = undefined; + + // WHEN + const result = comp.readableValue({ name: 'jhipster' }); + + // THEN + expect(result).toEqual('{"name":"jhipster"}'); + }); + + it('should return string value', () => { + // GIVEN + comp.health = undefined; + + // WHEN + const result = comp.readableValue('jhipster'); + + // THEN + expect(result).toEqual('jhipster'); + }); + + it('should return storage space in an human readable unit (GB)', () => { + // GIVEN + comp.health = { + key: 'diskSpace', + value: { + status: 'UP', + }, + }; + + // WHEN + const result = comp.readableValue(1073741825); + + // THEN + expect(result).toEqual('1.00 GB'); + }); + + it('should return storage space in an human readable unit (MB)', () => { + // GIVEN + comp.health = { + key: 'diskSpace', + value: { + status: 'UP', + }, + }; + + // WHEN + const result = comp.readableValue(1073741824); + + // THEN + expect(result).toEqual('1024.00 MB'); + }); + + it('should return string value', () => { + // GIVEN + comp.health = { + key: 'mail', + value: { + status: 'UP', + }, + }; + + // WHEN + const result = comp.readableValue(1234); + + // THEN + expect(result).toEqual('1234'); + }); + }); + + describe('dismiss', () => { + it('should call dismiss when dismiss modal is called', () => { + // GIVEN + const spy = jest.spyOn(mockActiveModal, 'dismiss'); + + // WHEN + comp.dismiss(); + + // THEN + expect(spy).toHaveBeenCalled(); + }); + }); +}); diff --git a/src/main/webapp/app/admin/health/modal/health-modal.component.ts b/src/main/webapp/app/admin/health/modal/health-modal.component.ts new file mode 100644 index 00000000..02f7cc94 --- /dev/null +++ b/src/main/webapp/app/admin/health/modal/health-modal.component.ts @@ -0,0 +1,34 @@ +import { Component } from '@angular/core'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; + +import { HealthKey, HealthDetails } from '../health.model'; + +@Component({ + selector: 'jhi-health-modal', + templateUrl: './health-modal.component.html', +}) +export class HealthModalComponent { + health?: { key: HealthKey; value: HealthDetails }; + + constructor(private activeModal: NgbActiveModal) {} + + readableValue(value: any): string { + if (this.health?.key === 'diskSpace') { + // Should display storage space in an human readable unit + const val = value / 1073741824; + if (val > 1) { + return `${val.toFixed(2)} GB`; + } + return `${(value / 1048576).toFixed(2)} MB`; + } + + if (typeof value === 'object') { + return JSON.stringify(value); + } + return String(value); + } + + dismiss(): void { + this.activeModal.dismiss(); + } +} diff --git a/src/main/webapp/app/admin/logs/log.model.ts b/src/main/webapp/app/admin/logs/log.model.ts new file mode 100644 index 00000000..83f2e154 --- /dev/null +++ b/src/main/webapp/app/admin/logs/log.model.ts @@ -0,0 +1,15 @@ +export type Level = 'TRACE' | 'DEBUG' | 'INFO' | 'WARN' | 'ERROR' | 'OFF'; + +export interface Logger { + configuredLevel: Level | null; + effectiveLevel: Level; +} + +export interface LoggersResponse { + levels: Level[]; + loggers: { [key: string]: Logger }; +} + +export class Log { + constructor(public name: string, public level: Level) {} +} diff --git a/src/main/webapp/app/admin/logs/logs.component.html b/src/main/webapp/app/admin/logs/logs.component.html new file mode 100644 index 00000000..59ada66d --- /dev/null +++ b/src/main/webapp/app/admin/logs/logs.component.html @@ -0,0 +1,74 @@ +
+

Logs

+ +

There are {{ loggers.length }} loggers.

+ + Filter + + + + + + + + + + + + + + + + +
Name Level
+ {{ logger.name | slice: 0:140 }} + + + + + + + + + + + + +
+
diff --git a/src/main/webapp/app/admin/logs/logs.component.spec.ts b/src/main/webapp/app/admin/logs/logs.component.spec.ts new file mode 100644 index 00000000..774e0188 --- /dev/null +++ b/src/main/webapp/app/admin/logs/logs.component.spec.ts @@ -0,0 +1,83 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { of } from 'rxjs'; + +import { LogsComponent } from './logs.component'; +import { LogsService } from './logs.service'; +import { Log, LoggersResponse } from './log.model'; + +describe('LogsComponent', () => { + let comp: LogsComponent; + let fixture: ComponentFixture; + let service: LogsService; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + declarations: [LogsComponent], + providers: [LogsService], + }) + .overrideTemplate(LogsComponent, '') + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(LogsComponent); + comp = fixture.componentInstance; + service = TestBed.inject(LogsService); + }); + + describe('OnInit', () => { + it('should set all default values correctly', () => { + expect(comp.filter).toBe(''); + expect(comp.orderProp).toBe('name'); + expect(comp.ascending).toBe(true); + }); + + it('Should call load all on init', () => { + // GIVEN + const log = new Log('main', 'WARN'); + jest.spyOn(service, 'findAll').mockReturnValue( + of({ + loggers: { + main: { + effectiveLevel: 'WARN', + }, + }, + } as unknown as LoggersResponse) + ); + + // WHEN + comp.ngOnInit(); + + // THEN + expect(service.findAll).toHaveBeenCalled(); + expect(comp.loggers?.[0]).toEqual(expect.objectContaining(log)); + }); + }); + + describe('change log level', () => { + it('should change log level correctly', () => { + // GIVEN + const log = new Log('main', 'ERROR'); + jest.spyOn(service, 'changeLevel').mockReturnValue(of({})); + jest.spyOn(service, 'findAll').mockReturnValue( + of({ + loggers: { + main: { + effectiveLevel: 'ERROR', + }, + }, + } as unknown as LoggersResponse) + ); + + // WHEN + comp.changeLevel('main', 'ERROR'); + + // THEN + expect(service.changeLevel).toHaveBeenCalled(); + expect(service.findAll).toHaveBeenCalled(); + expect(comp.loggers?.[0]).toEqual(expect.objectContaining(log)); + }); + }); +}); diff --git a/src/main/webapp/app/admin/logs/logs.component.ts b/src/main/webapp/app/admin/logs/logs.component.ts new file mode 100644 index 00000000..6320accb --- /dev/null +++ b/src/main/webapp/app/admin/logs/logs.component.ts @@ -0,0 +1,48 @@ +import { Component, OnInit } from '@angular/core'; + +import { Log, LoggersResponse, Level } from './log.model'; +import { LogsService } from './logs.service'; + +@Component({ + selector: 'jhi-logs', + templateUrl: './logs.component.html', +}) +export class LogsComponent implements OnInit { + loggers?: Log[]; + filteredAndOrderedLoggers?: Log[]; + filter = ''; + orderProp: keyof Log = 'name'; + ascending = true; + + constructor(private logsService: LogsService) {} + + ngOnInit(): void { + this.findAndExtractLoggers(); + } + + changeLevel(name: string, level: Level): void { + this.logsService.changeLevel(name, level).subscribe(() => this.findAndExtractLoggers()); + } + + filterAndSort(): void { + this.filteredAndOrderedLoggers = this.loggers!.filter( + logger => !this.filter || logger.name.toLowerCase().includes(this.filter.toLowerCase()) + ).sort((a, b) => { + if (a[this.orderProp] < b[this.orderProp]) { + return this.ascending ? -1 : 1; + } else if (a[this.orderProp] > b[this.orderProp]) { + return this.ascending ? 1 : -1; + } else if (this.orderProp === 'level') { + return a.name < b.name ? -1 : 1; + } + return 0; + }); + } + + private findAndExtractLoggers(): void { + this.logsService.findAll().subscribe((response: LoggersResponse) => { + this.loggers = Object.entries(response.loggers).map(([key, logger]) => new Log(key, logger.effectiveLevel)); + this.filterAndSort(); + }); + } +} diff --git a/src/main/webapp/app/admin/logs/logs.module.ts b/src/main/webapp/app/admin/logs/logs.module.ts new file mode 100644 index 00000000..720af279 --- /dev/null +++ b/src/main/webapp/app/admin/logs/logs.module.ts @@ -0,0 +1,12 @@ +import { NgModule } from '@angular/core'; +import { RouterModule } from '@angular/router'; +import { SharedModule } from 'app/shared/shared.module'; + +import { LogsComponent } from './logs.component'; +import { logsRoute } from './logs.route'; + +@NgModule({ + imports: [SharedModule, RouterModule.forChild([logsRoute])], + declarations: [LogsComponent], +}) +export class LogsModule {} diff --git a/src/main/webapp/app/admin/logs/logs.route.ts b/src/main/webapp/app/admin/logs/logs.route.ts new file mode 100644 index 00000000..a8ffdcdf --- /dev/null +++ b/src/main/webapp/app/admin/logs/logs.route.ts @@ -0,0 +1,11 @@ +import { Route } from '@angular/router'; + +import { LogsComponent } from './logs.component'; + +export const logsRoute: Route = { + path: '', + component: LogsComponent, + data: { + pageTitle: 'logs.title', + }, +}; diff --git a/src/main/webapp/app/admin/logs/logs.service.spec.ts b/src/main/webapp/app/admin/logs/logs.service.spec.ts new file mode 100644 index 00000000..cebee2cc --- /dev/null +++ b/src/main/webapp/app/admin/logs/logs.service.spec.ts @@ -0,0 +1,31 @@ +import { TestBed } from '@angular/core/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; + +import { LogsService } from './logs.service'; + +describe('Logs Service', () => { + let service: LogsService; + let httpMock: HttpTestingController; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + }); + + service = TestBed.inject(LogsService); + httpMock = TestBed.inject(HttpTestingController); + }); + + afterEach(() => { + httpMock.verify(); + }); + + describe('Service methods', () => { + it('should change log level', () => { + service.changeLevel('main', 'ERROR').subscribe(); + + const req = httpMock.expectOne({ method: 'POST' }); + expect(req.request.body).toEqual({ configuredLevel: 'ERROR' }); + }); + }); +}); diff --git a/src/main/webapp/app/admin/logs/logs.service.ts b/src/main/webapp/app/admin/logs/logs.service.ts new file mode 100644 index 00000000..625868a8 --- /dev/null +++ b/src/main/webapp/app/admin/logs/logs.service.ts @@ -0,0 +1,19 @@ +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { Observable } from 'rxjs'; + +import { ApplicationConfigService } from 'app/core/config/application-config.service'; +import { LoggersResponse, Level } from './log.model'; + +@Injectable({ providedIn: 'root' }) +export class LogsService { + constructor(private http: HttpClient, private applicationConfigService: ApplicationConfigService) {} + + changeLevel(name: string, configuredLevel: Level): Observable<{}> { + return this.http.post(this.applicationConfigService.getEndpointFor(`management/loggers/${name}`), { configuredLevel }); + } + + findAll(): Observable { + return this.http.get(this.applicationConfigService.getEndpointFor('management/loggers')); + } +} diff --git a/src/main/webapp/app/admin/metrics/blocks/jvm-memory/jvm-memory.component.html b/src/main/webapp/app/admin/metrics/blocks/jvm-memory/jvm-memory.component.html new file mode 100644 index 00000000..8e802cce --- /dev/null +++ b/src/main/webapp/app/admin/metrics/blocks/jvm-memory/jvm-memory.component.html @@ -0,0 +1,28 @@ +

Memory

+ +
+
+ + {{ entry.key }} + ({{ entry.value.used / 1048576 | number: '1.0-0' }}M / {{ entry.value.max / 1048576 | number: '1.0-0' }}M) + + +
Committed : {{ entry.value.committed / 1048576 | number: '1.0-0' }}M
+ + {{ entry.key }} {{ entry.value.used / 1048576 | number: '1.0-0' }}M + + + {{ (entry.value.used * 100) / entry.value.max | number: '1.0-0' }}% + +
+
diff --git a/src/main/webapp/app/admin/metrics/blocks/jvm-memory/jvm-memory.component.ts b/src/main/webapp/app/admin/metrics/blocks/jvm-memory/jvm-memory.component.ts new file mode 100644 index 00000000..d434b8ae --- /dev/null +++ b/src/main/webapp/app/admin/metrics/blocks/jvm-memory/jvm-memory.component.ts @@ -0,0 +1,19 @@ +import { Component, Input } from '@angular/core'; + +import { JvmMetrics } from 'app/admin/metrics/metrics.model'; + +@Component({ + selector: 'jhi-jvm-memory', + templateUrl: './jvm-memory.component.html', +}) +export class JvmMemoryComponent { + /** + * object containing all jvm memory metrics + */ + @Input() jvmMemoryMetrics?: { [key: string]: JvmMetrics }; + + /** + * boolean field saying if the metrics are in the process of being updated + */ + @Input() updating?: boolean; +} diff --git a/src/main/webapp/app/admin/metrics/blocks/jvm-threads/jvm-threads.component.html b/src/main/webapp/app/admin/metrics/blocks/jvm-threads/jvm-threads.component.html new file mode 100644 index 00000000..13bbe610 --- /dev/null +++ b/src/main/webapp/app/admin/metrics/blocks/jvm-threads/jvm-threads.component.html @@ -0,0 +1,55 @@ +

Threads

+ +Runnable {{ threadStats.threadDumpRunnable }} + + + {{ (threadStats.threadDumpRunnable * 100) / threadStats.threadDumpAll | number: '1.0-0' }}% + + +Timed waiting ({{ threadStats.threadDumpTimedWaiting }}) + + + {{ (threadStats.threadDumpTimedWaiting * 100) / threadStats.threadDumpAll | number: '1.0-0' }}% + + +Waiting ({{ threadStats.threadDumpWaiting }}) + + + {{ (threadStats.threadDumpWaiting * 100) / threadStats.threadDumpAll | number: '1.0-0' }}% + + +Blocked ({{ threadStats.threadDumpBlocked }}) + + + {{ (threadStats.threadDumpBlocked * 100) / threadStats.threadDumpAll | number: '1.0-0' }}% + + +
Total: {{ threadStats.threadDumpAll }}
+ + diff --git a/src/main/webapp/app/admin/metrics/blocks/jvm-threads/jvm-threads.component.ts b/src/main/webapp/app/admin/metrics/blocks/jvm-threads/jvm-threads.component.ts new file mode 100644 index 00000000..de98fb26 --- /dev/null +++ b/src/main/webapp/app/admin/metrics/blocks/jvm-threads/jvm-threads.component.ts @@ -0,0 +1,55 @@ +import { Component, Input } from '@angular/core'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; + +import { Thread, ThreadState } from 'app/admin/metrics/metrics.model'; +import { MetricsModalThreadsComponent } from '../metrics-modal-threads/metrics-modal-threads.component'; + +@Component({ + selector: 'jhi-jvm-threads', + templateUrl: './jvm-threads.component.html', +}) +export class JvmThreadsComponent { + threadStats = { + threadDumpAll: 0, + threadDumpRunnable: 0, + threadDumpTimedWaiting: 0, + threadDumpWaiting: 0, + threadDumpBlocked: 0, + }; + + @Input() + set threads(threads: Thread[] | undefined) { + this._threads = threads; + + threads?.forEach(thread => { + if (thread.threadState === ThreadState.Runnable) { + this.threadStats.threadDumpRunnable += 1; + } else if (thread.threadState === ThreadState.Waiting) { + this.threadStats.threadDumpWaiting += 1; + } else if (thread.threadState === ThreadState.TimedWaiting) { + this.threadStats.threadDumpTimedWaiting += 1; + } else if (thread.threadState === ThreadState.Blocked) { + this.threadStats.threadDumpBlocked += 1; + } + }); + + this.threadStats.threadDumpAll = + this.threadStats.threadDumpRunnable + + this.threadStats.threadDumpWaiting + + this.threadStats.threadDumpTimedWaiting + + this.threadStats.threadDumpBlocked; + } + + get threads(): Thread[] | undefined { + return this._threads; + } + + private _threads: Thread[] | undefined; + + constructor(private modalService: NgbModal) {} + + open(): void { + const modalRef = this.modalService.open(MetricsModalThreadsComponent); + modalRef.componentInstance.threads = this.threads; + } +} diff --git a/src/main/webapp/app/admin/metrics/blocks/metrics-cache/metrics-cache.component.html b/src/main/webapp/app/admin/metrics/blocks/metrics-cache/metrics-cache.component.html new file mode 100644 index 00000000..18f1bcbd --- /dev/null +++ b/src/main/webapp/app/admin/metrics/blocks/metrics-cache/metrics-cache.component.html @@ -0,0 +1,42 @@ +

Cache statistics

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
Cache nameCache HitsCache MissesCache GetsCache PutsCache RemovalsCache EvictionsCache Hit %Cache Miss %
{{ entry.key }}{{ entry.value['cache.gets.hit'] }}{{ entry.value['cache.gets.miss'] }}{{ entry.value['cache.gets.hit'] + entry.value['cache.gets.miss'] }}{{ entry.value['cache.puts'] }}{{ entry.value['cache.removals'] }}{{ entry.value['cache.evictions'] }} + {{ + filterNaN((100 * entry.value['cache.gets.hit']) / (entry.value['cache.gets.hit'] + entry.value['cache.gets.miss'])) + | number: '1.0-4' + }} + + {{ + filterNaN((100 * entry.value['cache.gets.miss']) / (entry.value['cache.gets.hit'] + entry.value['cache.gets.miss'])) + | number: '1.0-4' + }} +
+
diff --git a/src/main/webapp/app/admin/metrics/blocks/metrics-cache/metrics-cache.component.ts b/src/main/webapp/app/admin/metrics/blocks/metrics-cache/metrics-cache.component.ts new file mode 100644 index 00000000..6d721463 --- /dev/null +++ b/src/main/webapp/app/admin/metrics/blocks/metrics-cache/metrics-cache.component.ts @@ -0,0 +1,23 @@ +import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; + +import { CacheMetrics } from 'app/admin/metrics/metrics.model'; +import { filterNaN } from 'app/core/util/operators'; + +@Component({ + selector: 'jhi-metrics-cache', + templateUrl: './metrics-cache.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class MetricsCacheComponent { + /** + * object containing all cache related metrics + */ + @Input() cacheMetrics?: { [key: string]: CacheMetrics }; + + /** + * boolean field saying if the metrics are in the process of being updated + */ + @Input() updating?: boolean; + + filterNaN = (input: number): number => filterNaN(input); +} diff --git a/src/main/webapp/app/admin/metrics/blocks/metrics-datasource/metrics-datasource.component.html b/src/main/webapp/app/admin/metrics/blocks/metrics-datasource/metrics-datasource.component.html new file mode 100644 index 00000000..ab524b86 --- /dev/null +++ b/src/main/webapp/app/admin/metrics/blocks/metrics-datasource/metrics-datasource.component.html @@ -0,0 +1,57 @@ +

DataSource statistics (time in millisecond)

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Connection Pool Usage (active: {{ datasourceMetrics.active.value }}, min: + {{ datasourceMetrics.min.value }}, max: {{ datasourceMetrics.max.value }}, idle: {{ datasourceMetrics.idle.value }}) + CountMeanMinp50p75p95p99Max
Acquire{{ datasourceMetrics.acquire.count }}{{ filterNaN(datasourceMetrics.acquire.mean) | number: '1.0-2' }}{{ datasourceMetrics.acquire['0.0'] | number: '1.0-3' }}{{ datasourceMetrics.acquire['0.5'] | number: '1.0-3' }}{{ datasourceMetrics.acquire['0.75'] | number: '1.0-3' }}{{ datasourceMetrics.acquire['0.95'] | number: '1.0-3' }}{{ datasourceMetrics.acquire['0.99'] | number: '1.0-3' }}{{ filterNaN(datasourceMetrics.acquire.max) | number: '1.0-2' }}
Creation{{ datasourceMetrics.creation.count }}{{ filterNaN(datasourceMetrics.creation.mean) | number: '1.0-2' }}{{ datasourceMetrics.creation['0.0'] | number: '1.0-3' }}{{ datasourceMetrics.creation['0.5'] | number: '1.0-3' }}{{ datasourceMetrics.creation['0.75'] | number: '1.0-3' }}{{ datasourceMetrics.creation['0.95'] | number: '1.0-3' }}{{ datasourceMetrics.creation['0.99'] | number: '1.0-3' }}{{ filterNaN(datasourceMetrics.creation.max) | number: '1.0-2' }}
Usage{{ datasourceMetrics.usage.count }}{{ filterNaN(datasourceMetrics.usage.mean) | number: '1.0-2' }}{{ datasourceMetrics.usage['0.0'] | number: '1.0-3' }}{{ datasourceMetrics.usage['0.5'] | number: '1.0-3' }}{{ datasourceMetrics.usage['0.75'] | number: '1.0-3' }}{{ datasourceMetrics.usage['0.95'] | number: '1.0-3' }}{{ datasourceMetrics.usage['0.99'] | number: '1.0-3' }}{{ filterNaN(datasourceMetrics.usage.max) | number: '1.0-2' }}
+
diff --git a/src/main/webapp/app/admin/metrics/blocks/metrics-datasource/metrics-datasource.component.ts b/src/main/webapp/app/admin/metrics/blocks/metrics-datasource/metrics-datasource.component.ts new file mode 100644 index 00000000..eefcf585 --- /dev/null +++ b/src/main/webapp/app/admin/metrics/blocks/metrics-datasource/metrics-datasource.component.ts @@ -0,0 +1,23 @@ +import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; + +import { Databases } from 'app/admin/metrics/metrics.model'; +import { filterNaN } from 'app/core/util/operators'; + +@Component({ + selector: 'jhi-metrics-datasource', + templateUrl: './metrics-datasource.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class MetricsDatasourceComponent { + /** + * object containing all datasource related metrics + */ + @Input() datasourceMetrics?: Databases; + + /** + * boolean field saying if the metrics are in the process of being updated + */ + @Input() updating?: boolean; + + filterNaN = (input: number): number => filterNaN(input); +} diff --git a/src/main/webapp/app/admin/metrics/blocks/metrics-endpoints-requests/metrics-endpoints-requests.component.html b/src/main/webapp/app/admin/metrics/blocks/metrics-endpoints-requests/metrics-endpoints-requests.component.html new file mode 100644 index 00000000..ee47ec7e --- /dev/null +++ b/src/main/webapp/app/admin/metrics/blocks/metrics-endpoints-requests/metrics-endpoints-requests.component.html @@ -0,0 +1,24 @@ +

Endpoints requests (time in millisecond)

+ +
+ + + + + + + + + + + + + + + + + + + +
MethodEndpoint urlCountMean
{{ method.key }}{{ entry.key }}{{ method.value!.count }}{{ method.value!.mean | number: '1.0-3' }}
+
diff --git a/src/main/webapp/app/admin/metrics/blocks/metrics-endpoints-requests/metrics-endpoints-requests.component.ts b/src/main/webapp/app/admin/metrics/blocks/metrics-endpoints-requests/metrics-endpoints-requests.component.ts new file mode 100644 index 00000000..aa4ceb4b --- /dev/null +++ b/src/main/webapp/app/admin/metrics/blocks/metrics-endpoints-requests/metrics-endpoints-requests.component.ts @@ -0,0 +1,19 @@ +import { Component, Input } from '@angular/core'; + +import { Services } from 'app/admin/metrics/metrics.model'; + +@Component({ + selector: 'jhi-metrics-endpoints-requests', + templateUrl: './metrics-endpoints-requests.component.html', +}) +export class MetricsEndpointsRequestsComponent { + /** + * object containing service related metrics + */ + @Input() endpointsRequestsMetrics?: Services; + + /** + * boolean field saying if the metrics are in the process of being updated + */ + @Input() updating?: boolean; +} diff --git a/src/main/webapp/app/admin/metrics/blocks/metrics-garbagecollector/metrics-garbagecollector.component.html b/src/main/webapp/app/admin/metrics/blocks/metrics-garbagecollector/metrics-garbagecollector.component.html new file mode 100644 index 00000000..85b86190 --- /dev/null +++ b/src/main/webapp/app/admin/metrics/blocks/metrics-garbagecollector/metrics-garbagecollector.component.html @@ -0,0 +1,92 @@ +

Garbage collections

+ +
+
+
+ + GC Live Data Size/GC Max Data Size ({{ garbageCollectorMetrics['jvm.gc.live.data.size'] / 1048576 | number: '1.0-0' }}M / + {{ garbageCollectorMetrics['jvm.gc.max.data.size'] / 1048576 | number: '1.0-0' }}M) + + + + + {{ + (100 * garbageCollectorMetrics['jvm.gc.live.data.size']) / garbageCollectorMetrics['jvm.gc.max.data.size'] | number: '1.0-2' + }}% + + +
+
+ +
+
+ + GC Memory Promoted/GC Memory Allocated ({{ garbageCollectorMetrics['jvm.gc.memory.promoted'] / 1048576 | number: '1.0-0' }}M / + {{ garbageCollectorMetrics['jvm.gc.memory.allocated'] / 1048576 | number: '1.0-0' }}M) + + + + + {{ + (100 * garbageCollectorMetrics['jvm.gc.memory.promoted']) / garbageCollectorMetrics['jvm.gc.memory.allocated'] + | number: '1.0-2' + }}% + + +
+
+ +
+
+
Classes loaded
+
{{ garbageCollectorMetrics.classesLoaded }}
+
+
+
Classes unloaded
+
{{ garbageCollectorMetrics.classesUnloaded }}
+
+
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
CountMeanMinp50p75p95p99Max
jvm.gc.pause{{ garbageCollectorMetrics['jvm.gc.pause'].count }}{{ garbageCollectorMetrics['jvm.gc.pause'].mean | number: '1.0-3' }}{{ garbageCollectorMetrics['jvm.gc.pause']['0.0'] | number: '1.0-3' }}{{ garbageCollectorMetrics['jvm.gc.pause']['0.5'] | number: '1.0-3' }}{{ garbageCollectorMetrics['jvm.gc.pause']['0.75'] | number: '1.0-3' }}{{ garbageCollectorMetrics['jvm.gc.pause']['0.95'] | number: '1.0-3' }}{{ garbageCollectorMetrics['jvm.gc.pause']['0.99'] | number: '1.0-3' }}{{ garbageCollectorMetrics['jvm.gc.pause'].max | number: '1.0-3' }}
+
+
diff --git a/src/main/webapp/app/admin/metrics/blocks/metrics-garbagecollector/metrics-garbagecollector.component.ts b/src/main/webapp/app/admin/metrics/blocks/metrics-garbagecollector/metrics-garbagecollector.component.ts new file mode 100644 index 00000000..bbf11e00 --- /dev/null +++ b/src/main/webapp/app/admin/metrics/blocks/metrics-garbagecollector/metrics-garbagecollector.component.ts @@ -0,0 +1,19 @@ +import { Component, Input } from '@angular/core'; + +import { GarbageCollector } from 'app/admin/metrics/metrics.model'; + +@Component({ + selector: 'jhi-metrics-garbagecollector', + templateUrl: './metrics-garbagecollector.component.html', +}) +export class MetricsGarbageCollectorComponent { + /** + * object containing garbage collector related metrics + */ + @Input() garbageCollectorMetrics?: GarbageCollector; + + /** + * boolean field saying if the metrics are in the process of being updated + */ + @Input() updating?: boolean; +} diff --git a/src/main/webapp/app/admin/metrics/blocks/metrics-modal-threads/metrics-modal-threads.component.html b/src/main/webapp/app/admin/metrics/blocks/metrics-modal-threads/metrics-modal-threads.component.html new file mode 100644 index 00000000..b53179be --- /dev/null +++ b/src/main/webapp/app/admin/metrics/blocks/metrics-modal-threads/metrics-modal-threads.component.html @@ -0,0 +1,90 @@ + + + + diff --git a/src/main/webapp/app/admin/metrics/blocks/metrics-modal-threads/metrics-modal-threads.component.ts b/src/main/webapp/app/admin/metrics/blocks/metrics-modal-threads/metrics-modal-threads.component.ts new file mode 100644 index 00000000..f6dd2816 --- /dev/null +++ b/src/main/webapp/app/admin/metrics/blocks/metrics-modal-threads/metrics-modal-threads.component.ts @@ -0,0 +1,59 @@ +import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; + +import { Thread, ThreadState } from 'app/admin/metrics/metrics.model'; + +@Component({ + selector: 'jhi-thread-modal', + templateUrl: './metrics-modal-threads.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class MetricsModalThreadsComponent implements OnInit { + ThreadState = ThreadState; + threadStateFilter?: ThreadState; + threads?: Thread[]; + threadDumpAll = 0; + threadDumpBlocked = 0; + threadDumpRunnable = 0; + threadDumpTimedWaiting = 0; + threadDumpWaiting = 0; + + constructor(private activeModal: NgbActiveModal) {} + + ngOnInit(): void { + this.threads?.forEach(thread => { + if (thread.threadState === ThreadState.Runnable) { + this.threadDumpRunnable += 1; + } else if (thread.threadState === ThreadState.Waiting) { + this.threadDumpWaiting += 1; + } else if (thread.threadState === ThreadState.TimedWaiting) { + this.threadDumpTimedWaiting += 1; + } else if (thread.threadState === ThreadState.Blocked) { + this.threadDumpBlocked += 1; + } + }); + + this.threadDumpAll = this.threadDumpRunnable + this.threadDumpWaiting + this.threadDumpTimedWaiting + this.threadDumpBlocked; + } + + getBadgeClass(threadState: ThreadState): string { + if (threadState === ThreadState.Runnable) { + return 'bg-success'; + } else if (threadState === ThreadState.Waiting) { + return 'bg-info'; + } else if (threadState === ThreadState.TimedWaiting) { + return 'bg-warning'; + } else if (threadState === ThreadState.Blocked) { + return 'bg-danger'; + } + return ''; + } + + getThreads(): Thread[] { + return this.threads?.filter(thread => !this.threadStateFilter || thread.threadState === this.threadStateFilter) ?? []; + } + + dismiss(): void { + this.activeModal.dismiss(); + } +} diff --git a/src/main/webapp/app/admin/metrics/blocks/metrics-request/metrics-request.component.html b/src/main/webapp/app/admin/metrics/blocks/metrics-request/metrics-request.component.html new file mode 100644 index 00000000..275acdf9 --- /dev/null +++ b/src/main/webapp/app/admin/metrics/blocks/metrics-request/metrics-request.component.html @@ -0,0 +1,26 @@ +

HTTP requests (time in millisecond)

+ + + + + + + + + + + + + + + + + + +
CodeCountMeanMax
{{ entry.key }} + + {{ entry.value.count }} + + + {{ filterNaN(entry.value.mean) | number: '1.0-2' }} + {{ entry.value.max | number: '1.0-2' }}
diff --git a/src/main/webapp/app/admin/metrics/blocks/metrics-request/metrics-request.component.ts b/src/main/webapp/app/admin/metrics/blocks/metrics-request/metrics-request.component.ts new file mode 100644 index 00000000..6a7cbc7d --- /dev/null +++ b/src/main/webapp/app/admin/metrics/blocks/metrics-request/metrics-request.component.ts @@ -0,0 +1,23 @@ +import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; + +import { HttpServerRequests } from 'app/admin/metrics/metrics.model'; +import { filterNaN } from 'app/core/util/operators'; + +@Component({ + selector: 'jhi-metrics-request', + templateUrl: './metrics-request.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class MetricsRequestComponent { + /** + * object containing http request related metrics + */ + @Input() requestMetrics?: HttpServerRequests; + + /** + * boolean field saying if the metrics are in the process of being updated + */ + @Input() updating?: boolean; + + filterNaN = (input: number): number => filterNaN(input); +} diff --git a/src/main/webapp/app/admin/metrics/blocks/metrics-system/metrics-system.component.html b/src/main/webapp/app/admin/metrics/blocks/metrics-system/metrics-system.component.html new file mode 100644 index 00000000..d35be0cf --- /dev/null +++ b/src/main/webapp/app/admin/metrics/blocks/metrics-system/metrics-system.component.html @@ -0,0 +1,51 @@ +

System

+ + +
+
Uptime
+
{{ convertMillisecondsToDuration(systemMetrics['process.uptime']) }}
+
+ +
+
Start time
+
{{ systemMetrics['process.start.time'] | date: 'full' }}
+
+ +
+
Process CPU usage
+
{{ 100 * systemMetrics['process.cpu.usage'] | number: '1.0-2' }} %
+
+ + + {{ 100 * systemMetrics['process.cpu.usage'] | number: '1.0-2' }} % + + +
+
System CPU usage
+
{{ 100 * systemMetrics['system.cpu.usage'] | number: '1.0-2' }} %
+
+ + + {{ 100 * systemMetrics['system.cpu.usage'] | number: '1.0-2' }} % + + +
+
System CPU count
+
{{ systemMetrics['system.cpu.count'] }}
+
+ +
+
System 1m Load average
+
{{ systemMetrics['system.load.average.1m'] | number: '1.0-2' }}
+
+ +
+
Process files max
+
{{ systemMetrics['process.files.max'] | number: '1.0-0' }}
+
+ +
+
Process files open
+
{{ systemMetrics['process.files.open'] | number: '1.0-0' }}
+
+
diff --git a/src/main/webapp/app/admin/metrics/blocks/metrics-system/metrics-system.component.ts b/src/main/webapp/app/admin/metrics/blocks/metrics-system/metrics-system.component.ts new file mode 100644 index 00000000..f078ada4 --- /dev/null +++ b/src/main/webapp/app/admin/metrics/blocks/metrics-system/metrics-system.component.ts @@ -0,0 +1,43 @@ +import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; + +import { ProcessMetrics } from 'app/admin/metrics/metrics.model'; + +@Component({ + selector: 'jhi-metrics-system', + templateUrl: './metrics-system.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class MetricsSystemComponent { + /** + * object containing thread related metrics + */ + @Input() systemMetrics?: ProcessMetrics; + + /** + * boolean field saying if the metrics are in the process of being updated + */ + @Input() updating?: boolean; + + convertMillisecondsToDuration(ms: number): string { + const times = { + year: 31557600000, + month: 2629746000, + day: 86400000, + hour: 3600000, + minute: 60000, + second: 1000, + }; + let timeString = ''; + for (const [key, value] of Object.entries(times)) { + if (Math.floor(ms / value) > 0) { + let plural = ''; + if (Math.floor(ms / value) > 1) { + plural = 's'; + } + timeString += `${Math.floor(ms / value).toString()} ${key.toString()}${plural} `; + ms = ms - value * Math.floor(ms / value); + } + } + return timeString; + } +} diff --git a/src/main/webapp/app/admin/metrics/metrics.component.html b/src/main/webapp/app/admin/metrics/metrics.component.html new file mode 100644 index 00000000..c59876ce --- /dev/null +++ b/src/main/webapp/app/admin/metrics/metrics.component.html @@ -0,0 +1,51 @@ +
+

+ Application Metrics + + +

+ +

JVM Metrics

+ +
+ + + + + +
+ + + +
Updating...
+ + + + + + + + +
diff --git a/src/main/webapp/app/admin/metrics/metrics.component.spec.ts b/src/main/webapp/app/admin/metrics/metrics.component.spec.ts new file mode 100644 index 00000000..4bd32586 --- /dev/null +++ b/src/main/webapp/app/admin/metrics/metrics.component.spec.ts @@ -0,0 +1,41 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { of } from 'rxjs'; + +import { MetricsComponent } from './metrics.component'; +import { MetricsService } from './metrics.service'; +import { Metrics } from './metrics.model'; + +describe('MetricsComponent', () => { + let comp: MetricsComponent; + let fixture: ComponentFixture; + let service: MetricsService; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + declarations: [MetricsComponent], + }) + .overrideTemplate(MetricsComponent, '') + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(MetricsComponent); + comp = fixture.componentInstance; + service = TestBed.inject(MetricsService); + }); + + describe('refresh', () => { + it('should call refresh on init', () => { + // GIVEN + jest.spyOn(service, 'getMetrics').mockReturnValue(of({} as Metrics)); + + // WHEN + comp.ngOnInit(); + + // THEN + expect(service.getMetrics).toHaveBeenCalled(); + }); + }); +}); diff --git a/src/main/webapp/app/admin/metrics/metrics.component.ts b/src/main/webapp/app/admin/metrics/metrics.component.ts new file mode 100644 index 00000000..f633ee1f --- /dev/null +++ b/src/main/webapp/app/admin/metrics/metrics.component.ts @@ -0,0 +1,40 @@ +import { Component, OnInit, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/core'; +import { combineLatest } from 'rxjs'; + +import { MetricsService } from './metrics.service'; +import { Metrics, Thread } from './metrics.model'; + +@Component({ + selector: 'jhi-metrics', + templateUrl: './metrics.component.html', + changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class MetricsComponent implements OnInit { + metrics?: Metrics; + threads?: Thread[]; + updatingMetrics = true; + + constructor(private metricsService: MetricsService, private changeDetector: ChangeDetectorRef) {} + + ngOnInit(): void { + this.refresh(); + } + + refresh(): void { + this.updatingMetrics = true; + combineLatest([this.metricsService.getMetrics(), this.metricsService.threadDump()]).subscribe(([metrics, threadDump]) => { + this.metrics = metrics; + this.threads = threadDump.threads; + this.updatingMetrics = false; + this.changeDetector.markForCheck(); + }); + } + + metricsKeyExists(key: keyof Metrics): boolean { + return Boolean(this.metrics?.[key]); + } + + metricsKeyExistsAndObjectNotEmpty(key: keyof Metrics): boolean { + return Boolean(this.metrics?.[key] && JSON.stringify(this.metrics[key]) !== '{}'); + } +} diff --git a/src/main/webapp/app/admin/metrics/metrics.model.ts b/src/main/webapp/app/admin/metrics/metrics.model.ts new file mode 100644 index 00000000..d9576a90 --- /dev/null +++ b/src/main/webapp/app/admin/metrics/metrics.model.ts @@ -0,0 +1,159 @@ +export interface Metrics { + jvm: { [key: string]: JvmMetrics }; + databases: Databases; + 'http.server.requests': HttpServerRequests; + cache: { [key: string]: CacheMetrics }; + garbageCollector: GarbageCollector; + services: Services; + processMetrics: ProcessMetrics; +} + +export interface JvmMetrics { + committed: number; + max: number; + used: number; +} + +export interface Databases { + min: Value; + idle: Value; + max: Value; + usage: MetricsWithPercentile; + pending: Value; + active: Value; + acquire: MetricsWithPercentile; + creation: MetricsWithPercentile; + connections: Value; +} + +export interface Value { + value: number; +} + +export interface MetricsWithPercentile { + '0.0': number; + '1.0': number; + max: number; + totalTime: number; + mean: number; + '0.5': number; + count: number; + '0.99': number; + '0.75': number; + '0.95': number; +} + +export interface HttpServerRequests { + all: { + count: number; + }; + percode: { [key: string]: MaxMeanCount }; +} + +export interface MaxMeanCount { + max: number; + mean: number; + count: number; +} + +export interface CacheMetrics { + 'cache.gets.miss': number; + 'cache.puts': number; + 'cache.gets.hit': number; + 'cache.removals': number; + 'cache.evictions': number; +} + +export interface GarbageCollector { + 'jvm.gc.max.data.size': number; + 'jvm.gc.pause': MetricsWithPercentile; + 'jvm.gc.memory.promoted': number; + 'jvm.gc.memory.allocated': number; + classesLoaded: number; + 'jvm.gc.live.data.size': number; + classesUnloaded: number; +} + +export interface Services { + [key: string]: { + [key in HttpMethod]?: MaxMeanCount; + }; +} + +export enum HttpMethod { + Post = 'POST', + Get = 'GET', + Put = 'PUT', + Patch = 'PATCH', + Delete = 'DELETE', +} + +export interface ProcessMetrics { + 'system.cpu.usage': number; + 'system.cpu.count': number; + 'system.load.average.1m'?: number; + 'process.cpu.usage': number; + 'process.files.max'?: number; + 'process.files.open'?: number; + 'process.start.time': number; + 'process.uptime': number; +} + +export interface ThreadDump { + threads: Thread[]; +} + +export interface Thread { + threadName: string; + threadId: number; + blockedTime: number; + blockedCount: number; + waitedTime: number; + waitedCount: number; + lockName: string | null; + lockOwnerId: number; + lockOwnerName: string | null; + daemon: boolean; + inNative: boolean; + suspended: boolean; + threadState: ThreadState; + priority: number; + stackTrace: StackTrace[]; + lockedMonitors: LockedMonitor[]; + lockedSynchronizers: string[]; + lockInfo: LockInfo | null; + // custom field for showing-hiding thread dump + showThreadDump?: boolean; +} + +export interface LockInfo { + className: string; + identityHashCode: number; +} + +export interface LockedMonitor { + className: string; + identityHashCode: number; + lockedStackDepth: number; + lockedStackFrame: StackTrace; +} + +export interface StackTrace { + classLoaderName: string | null; + moduleName: string | null; + moduleVersion: string | null; + methodName: string; + fileName: string; + lineNumber: number; + className: string; + nativeMethod: boolean; +} + +export enum ThreadState { + Runnable = 'RUNNABLE', + TimedWaiting = 'TIMED_WAITING', + Waiting = 'WAITING', + Blocked = 'BLOCKED', + New = 'NEW', + Terminated = 'TERMINATED', +} diff --git a/src/main/webapp/app/admin/metrics/metrics.module.ts b/src/main/webapp/app/admin/metrics/metrics.module.ts new file mode 100644 index 00000000..e96c87b8 --- /dev/null +++ b/src/main/webapp/app/admin/metrics/metrics.module.ts @@ -0,0 +1,32 @@ +import { NgModule } from '@angular/core'; +import { RouterModule } from '@angular/router'; + +import { SharedModule } from 'app/shared/shared.module'; +import { MetricsComponent } from './metrics.component'; +import { metricsRoute } from './metrics.route'; +import { JvmMemoryComponent } from './blocks/jvm-memory/jvm-memory.component'; +import { JvmThreadsComponent } from './blocks/jvm-threads/jvm-threads.component'; +import { MetricsCacheComponent } from './blocks/metrics-cache/metrics-cache.component'; +import { MetricsDatasourceComponent } from './blocks/metrics-datasource/metrics-datasource.component'; +import { MetricsEndpointsRequestsComponent } from './blocks/metrics-endpoints-requests/metrics-endpoints-requests.component'; +import { MetricsGarbageCollectorComponent } from './blocks/metrics-garbagecollector/metrics-garbagecollector.component'; +import { MetricsModalThreadsComponent } from './blocks/metrics-modal-threads/metrics-modal-threads.component'; +import { MetricsRequestComponent } from './blocks/metrics-request/metrics-request.component'; +import { MetricsSystemComponent } from './blocks/metrics-system/metrics-system.component'; + +@NgModule({ + imports: [SharedModule, RouterModule.forChild([metricsRoute])], + declarations: [ + MetricsComponent, + JvmMemoryComponent, + JvmThreadsComponent, + MetricsCacheComponent, + MetricsDatasourceComponent, + MetricsEndpointsRequestsComponent, + MetricsGarbageCollectorComponent, + MetricsModalThreadsComponent, + MetricsRequestComponent, + MetricsSystemComponent, + ], +}) +export class MetricsModule {} diff --git a/src/main/webapp/app/admin/metrics/metrics.route.ts b/src/main/webapp/app/admin/metrics/metrics.route.ts new file mode 100644 index 00000000..c090cf6e --- /dev/null +++ b/src/main/webapp/app/admin/metrics/metrics.route.ts @@ -0,0 +1,11 @@ +import { Route } from '@angular/router'; + +import { MetricsComponent } from './metrics.component'; + +export const metricsRoute: Route = { + path: '', + component: MetricsComponent, + data: { + pageTitle: 'metrics.title', + }, +}; diff --git a/src/main/webapp/app/admin/metrics/metrics.service.spec.ts b/src/main/webapp/app/admin/metrics/metrics.service.spec.ts new file mode 100644 index 00000000..468ebd52 --- /dev/null +++ b/src/main/webapp/app/admin/metrics/metrics.service.spec.ts @@ -0,0 +1,81 @@ +import { TestBed } from '@angular/core/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; + +import { MetricsService } from './metrics.service'; +import { ThreadDump, ThreadState } from './metrics.model'; + +describe('Logs Service', () => { + let service: MetricsService; + let httpMock: HttpTestingController; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + }); + service = TestBed.inject(MetricsService); + httpMock = TestBed.inject(HttpTestingController); + }); + + afterEach(() => { + httpMock.verify(); + }); + + describe('Service methods', () => { + it('should return Metrics', () => { + let expectedResult; + const metrics = { + jvm: {}, + 'http.server.requests': {}, + cache: {}, + services: {}, + databases: {}, + garbageCollector: {}, + processMetrics: {}, + }; + + service.getMetrics().subscribe(received => { + expectedResult = received; + }); + + const req = httpMock.expectOne({ method: 'GET' }); + req.flush(metrics); + expect(expectedResult).toEqual(metrics); + }); + + it('should return Thread Dump', () => { + let expectedResult: ThreadDump | null = null; + const dump: ThreadDump = { + threads: [ + { + threadName: 'Reference Handler', + threadId: 2, + blockedTime: -1, + blockedCount: 7, + waitedTime: -1, + waitedCount: 0, + lockName: null, + lockOwnerId: -1, + lockOwnerName: null, + daemon: true, + inNative: false, + suspended: false, + threadState: ThreadState.Runnable, + priority: 10, + stackTrace: [], + lockedMonitors: [], + lockedSynchronizers: [], + lockInfo: null, + }, + ], + }; + + service.threadDump().subscribe(received => { + expectedResult = received; + }); + + const req = httpMock.expectOne({ method: 'GET' }); + req.flush(dump); + expect(expectedResult).toEqual(dump); + }); + }); +}); diff --git a/src/main/webapp/app/admin/metrics/metrics.service.ts b/src/main/webapp/app/admin/metrics/metrics.service.ts new file mode 100644 index 00000000..1d27f2b1 --- /dev/null +++ b/src/main/webapp/app/admin/metrics/metrics.service.ts @@ -0,0 +1,19 @@ +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { Observable } from 'rxjs'; + +import { ApplicationConfigService } from 'app/core/config/application-config.service'; +import { Metrics, ThreadDump } from './metrics.model'; + +@Injectable({ providedIn: 'root' }) +export class MetricsService { + constructor(private http: HttpClient, private applicationConfigService: ApplicationConfigService) {} + + getMetrics(): Observable { + return this.http.get(this.applicationConfigService.getEndpointFor('management/jhimetrics')); + } + + threadDump(): Observable { + return this.http.get(this.applicationConfigService.getEndpointFor('management/threaddump')); + } +} diff --git a/src/main/webapp/app/admin/user-management/delete/user-management-delete-dialog.component.html b/src/main/webapp/app/admin/user-management/delete/user-management-delete-dialog.component.html new file mode 100644 index 00000000..0ae0c95b --- /dev/null +++ b/src/main/webapp/app/admin/user-management/delete/user-management-delete-dialog.component.html @@ -0,0 +1,25 @@ +
+ + + + + +
diff --git a/src/main/webapp/app/admin/user-management/delete/user-management-delete-dialog.component.spec.ts b/src/main/webapp/app/admin/user-management/delete/user-management-delete-dialog.component.spec.ts new file mode 100644 index 00000000..cab7e41c --- /dev/null +++ b/src/main/webapp/app/admin/user-management/delete/user-management-delete-dialog.component.spec.ts @@ -0,0 +1,52 @@ +jest.mock('@ng-bootstrap/ng-bootstrap'); + +import { ComponentFixture, TestBed, waitForAsync, inject, fakeAsync, tick } from '@angular/core/testing'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import { of } from 'rxjs'; + +import { UserManagementService } from '../service/user-management.service'; + +import { UserManagementDeleteDialogComponent } from './user-management-delete-dialog.component'; + +describe('User Management Delete Component', () => { + let comp: UserManagementDeleteDialogComponent; + let fixture: ComponentFixture; + let service: UserManagementService; + let mockActiveModal: NgbActiveModal; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + declarations: [UserManagementDeleteDialogComponent], + providers: [NgbActiveModal], + }) + .overrideTemplate(UserManagementDeleteDialogComponent, '') + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(UserManagementDeleteDialogComponent); + comp = fixture.componentInstance; + service = TestBed.inject(UserManagementService); + mockActiveModal = TestBed.inject(NgbActiveModal); + }); + + describe('confirmDelete', () => { + it('Should call delete service on confirmDelete', inject( + [], + fakeAsync(() => { + // GIVEN + jest.spyOn(service, 'delete').mockReturnValue(of({})); + + // WHEN + comp.confirmDelete('user'); + tick(); + + // THEN + expect(service.delete).toHaveBeenCalledWith('user'); + expect(mockActiveModal.close).toHaveBeenCalledWith('deleted'); + }) + )); + }); +}); diff --git a/src/main/webapp/app/admin/user-management/delete/user-management-delete-dialog.component.ts b/src/main/webapp/app/admin/user-management/delete/user-management-delete-dialog.component.ts new file mode 100644 index 00000000..87b9824f --- /dev/null +++ b/src/main/webapp/app/admin/user-management/delete/user-management-delete-dialog.component.ts @@ -0,0 +1,25 @@ +import { Component } from '@angular/core'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; + +import { User } from '../user-management.model'; +import { UserManagementService } from '../service/user-management.service'; + +@Component({ + selector: 'jhi-user-mgmt-delete-dialog', + templateUrl: './user-management-delete-dialog.component.html', +}) +export class UserManagementDeleteDialogComponent { + user?: User; + + constructor(private userService: UserManagementService, private activeModal: NgbActiveModal) {} + + cancel(): void { + this.activeModal.dismiss(); + } + + confirmDelete(login: string): void { + this.userService.delete(login).subscribe(() => { + this.activeModal.close('deleted'); + }); + } +} diff --git a/src/main/webapp/app/admin/user-management/detail/user-management-detail.component.html b/src/main/webapp/app/admin/user-management/detail/user-management-detail.component.html new file mode 100644 index 00000000..10515ec2 --- /dev/null +++ b/src/main/webapp/app/admin/user-management/detail/user-management-detail.component.html @@ -0,0 +1,56 @@ +
+
+
+

+ User [{{ user.login }}] +

+ +
+
Login
+
+ {{ user.login }} + Activated + Deactivated +
+ +
First name
+
{{ user.firstName }}
+ +
Last name
+
{{ user.lastName }}
+ +
Email
+
{{ user.email }}
+ +
Language
+
{{ user.langKey }}
+ +
Created by
+
{{ user.createdBy }}
+ +
Created date
+
{{ user.createdDate | date: 'dd/MM/yy HH:mm' }}
+ +
Modified by
+
{{ user.lastModifiedBy }}
+ +
Modified date
+
{{ user.lastModifiedDate | date: 'dd/MM/yy HH:mm' }}
+ +
Profiles
+
+
    +
  • + {{ authority }} +
  • +
+
+
+ + +
+
+
diff --git a/src/main/webapp/app/admin/user-management/detail/user-management-detail.component.spec.ts b/src/main/webapp/app/admin/user-management/detail/user-management-detail.component.spec.ts new file mode 100644 index 00000000..09a13437 --- /dev/null +++ b/src/main/webapp/app/admin/user-management/detail/user-management-detail.component.spec.ts @@ -0,0 +1,56 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { ActivatedRoute } from '@angular/router'; +import { of } from 'rxjs'; + +import { Authority } from 'app/config/authority.constants'; +import { User } from '../user-management.model'; + +import { UserManagementDetailComponent } from './user-management-detail.component'; + +describe('User Management Detail Component', () => { + let comp: UserManagementDetailComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [UserManagementDetailComponent], + providers: [ + { + provide: ActivatedRoute, + useValue: { + data: of({ user: new User(123, 'user', 'first', 'last', 'first@last.com', true, 'en', [Authority.USER], 'admin') }), + }, + }, + ], + }) + .overrideTemplate(UserManagementDetailComponent, '') + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(UserManagementDetailComponent); + comp = fixture.componentInstance; + }); + + describe('OnInit', () => { + it('Should call load all on init', () => { + // WHEN + comp.ngOnInit(); + + // THEN + expect(comp.user).toEqual( + expect.objectContaining({ + id: 123, + login: 'user', + firstName: 'first', + lastName: 'last', + email: 'first@last.com', + activated: true, + langKey: 'en', + authorities: [Authority.USER], + createdBy: 'admin', + }) + ); + }); + }); +}); diff --git a/src/main/webapp/app/admin/user-management/detail/user-management-detail.component.ts b/src/main/webapp/app/admin/user-management/detail/user-management-detail.component.ts new file mode 100644 index 00000000..31f9b3ce --- /dev/null +++ b/src/main/webapp/app/admin/user-management/detail/user-management-detail.component.ts @@ -0,0 +1,20 @@ +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; + +import { User } from '../user-management.model'; + +@Component({ + selector: 'jhi-user-mgmt-detail', + templateUrl: './user-management-detail.component.html', +}) +export class UserManagementDetailComponent implements OnInit { + user: User | null = null; + + constructor(private route: ActivatedRoute) {} + + ngOnInit(): void { + this.route.data.subscribe(({ user }) => { + this.user = user; + }); + } +} diff --git a/src/main/webapp/app/admin/user-management/list/user-management.component.html b/src/main/webapp/app/admin/user-management/list/user-management.component.html new file mode 100644 index 00000000..041d7d6b --- /dev/null +++ b/src/main/webapp/app/admin/user-management/list/user-management.component.html @@ -0,0 +1,124 @@ +
+

+ Users + +
+ + +
+

+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ID Login Email + Language + Profiles + Created date + + Modified by + + Modified date +
+ {{ user.id }} + {{ user.login }}{{ user.email }} + + + {{ user.langKey }} +
+ {{ authority }} +
+
{{ user.createdDate | date: 'dd/MM/yy HH:mm' }}{{ user.lastModifiedBy }}{{ user.lastModifiedDate | date: 'dd/MM/yy HH:mm' }} +
+ + + + + +
+
+
+ +
+
+ +
+ +
+ +
+
+
diff --git a/src/main/webapp/app/admin/user-management/list/user-management.component.spec.ts b/src/main/webapp/app/admin/user-management/list/user-management.component.spec.ts new file mode 100644 index 00000000..634b8df0 --- /dev/null +++ b/src/main/webapp/app/admin/user-management/list/user-management.component.spec.ts @@ -0,0 +1,104 @@ +jest.mock('app/core/auth/account.service'); + +import { ComponentFixture, TestBed, waitForAsync, inject, fakeAsync, tick } from '@angular/core/testing'; +import { HttpHeaders, HttpResponse } from '@angular/common/http'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { ActivatedRoute } from '@angular/router'; +import { RouterTestingModule } from '@angular/router/testing'; +import { of } from 'rxjs'; + +import { UserManagementService } from '../service/user-management.service'; +import { User } from '../user-management.model'; +import { AccountService } from 'app/core/auth/account.service'; + +import { UserManagementComponent } from './user-management.component'; + +describe('User Management Component', () => { + let comp: UserManagementComponent; + let fixture: ComponentFixture; + let service: UserManagementService; + let mockAccountService: AccountService; + const data = of({ + defaultSort: 'id,asc', + }); + const queryParamMap = of( + jest.requireActual('@angular/router').convertToParamMap({ + page: '1', + size: '1', + sort: 'id,desc', + }) + ); + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule, RouterTestingModule.withRoutes([])], + declarations: [UserManagementComponent], + providers: [{ provide: ActivatedRoute, useValue: { data, queryParamMap } }, AccountService], + }) + .overrideTemplate(UserManagementComponent, '') + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(UserManagementComponent); + comp = fixture.componentInstance; + service = TestBed.inject(UserManagementService); + mockAccountService = TestBed.inject(AccountService); + mockAccountService.identity = jest.fn(() => of(null)); + }); + + describe('OnInit', () => { + it('Should call load all on init', inject( + [], + fakeAsync(() => { + // GIVEN + const headers = new HttpHeaders().append('link', 'link;link'); + jest.spyOn(service, 'query').mockReturnValue( + of( + new HttpResponse({ + body: [new User(123)], + headers, + }) + ) + ); + + // WHEN + comp.ngOnInit(); + tick(); // simulate async + + // THEN + expect(service.query).toHaveBeenCalled(); + expect(comp.users?.[0]).toEqual(expect.objectContaining({ id: 123 })); + }) + )); + }); + + describe('setActive', () => { + it('Should update user and call load all', inject( + [], + fakeAsync(() => { + // GIVEN + const headers = new HttpHeaders().append('link', 'link;link'); + const user = new User(123); + jest.spyOn(service, 'query').mockReturnValue( + of( + new HttpResponse({ + body: [user], + headers, + }) + ) + ); + jest.spyOn(service, 'update').mockReturnValue(of(user)); + + // WHEN + comp.setActive(user, true); + tick(); // simulate async + + // THEN + expect(service.update).toHaveBeenCalledWith({ ...user, activated: true }); + expect(service.query).toHaveBeenCalled(); + expect(comp.users?.[0]).toEqual(expect.objectContaining({ id: 123 })); + }) + )); + }); +}); diff --git a/src/main/webapp/app/admin/user-management/list/user-management.component.ts b/src/main/webapp/app/admin/user-management/list/user-management.component.ts new file mode 100644 index 00000000..5e3d0e98 --- /dev/null +++ b/src/main/webapp/app/admin/user-management/list/user-management.component.ts @@ -0,0 +1,111 @@ +import { Component, OnInit } from '@angular/core'; +import { HttpResponse, HttpHeaders } from '@angular/common/http'; +import { ActivatedRoute, Router } from '@angular/router'; +import { combineLatest } from 'rxjs'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; + +import { ITEMS_PER_PAGE } from 'app/config/pagination.constants'; +import { ASC, DESC, SORT } from 'app/config/navigation.constants'; +import { AccountService } from 'app/core/auth/account.service'; +import { Account } from 'app/core/auth/account.model'; +import { UserManagementService } from '../service/user-management.service'; +import { User } from '../user-management.model'; +import { UserManagementDeleteDialogComponent } from '../delete/user-management-delete-dialog.component'; + +@Component({ + selector: 'jhi-user-mgmt', + templateUrl: './user-management.component.html', +}) +export class UserManagementComponent implements OnInit { + currentAccount: Account | null = null; + users: User[] | null = null; + isLoading = false; + totalItems = 0; + itemsPerPage = ITEMS_PER_PAGE; + page!: number; + predicate!: string; + ascending!: boolean; + + constructor( + private userService: UserManagementService, + private accountService: AccountService, + private activatedRoute: ActivatedRoute, + private router: Router, + private modalService: NgbModal + ) {} + + ngOnInit(): void { + this.accountService.identity().subscribe(account => (this.currentAccount = account)); + this.handleNavigation(); + } + + setActive(user: User, isActivated: boolean): void { + this.userService.update({ ...user, activated: isActivated }).subscribe(() => this.loadAll()); + } + + trackIdentity(_index: number, item: User): number { + return item.id!; + } + + deleteUser(user: User): void { + const modalRef = this.modalService.open(UserManagementDeleteDialogComponent, { size: 'lg', backdrop: 'static' }); + modalRef.componentInstance.user = user; + // unsubscribe not needed because closed completes on modal close + modalRef.closed.subscribe(reason => { + if (reason === 'deleted') { + this.loadAll(); + } + }); + } + + loadAll(): void { + this.isLoading = true; + this.userService + .query({ + page: this.page - 1, + size: this.itemsPerPage, + sort: this.sort(), + }) + .subscribe({ + next: (res: HttpResponse) => { + this.isLoading = false; + this.onSuccess(res.body, res.headers); + }, + error: () => (this.isLoading = false), + }); + } + + transition(): void { + this.router.navigate(['./'], { + relativeTo: this.activatedRoute.parent, + queryParams: { + page: this.page, + sort: `${this.predicate},${this.ascending ? ASC : DESC}`, + }, + }); + } + + private handleNavigation(): void { + combineLatest([this.activatedRoute.data, this.activatedRoute.queryParamMap]).subscribe(([data, params]) => { + const page = params.get('page'); + this.page = +(page ?? 1); + const sort = (params.get(SORT) ?? data['defaultSort']).split(','); + this.predicate = sort[0]; + this.ascending = sort[1] === ASC; + this.loadAll(); + }); + } + + private sort(): string[] { + const result = [`${this.predicate},${this.ascending ? ASC : DESC}`]; + if (this.predicate !== 'id') { + result.push('id'); + } + return result; + } + + private onSuccess(users: User[] | null, headers: HttpHeaders): void { + this.totalItems = Number(headers.get('X-Total-Count')); + this.users = users; + } +} diff --git a/src/main/webapp/app/admin/user-management/service/user-management.service.spec.ts b/src/main/webapp/app/admin/user-management/service/user-management.service.spec.ts new file mode 100644 index 00000000..41a6ec89 --- /dev/null +++ b/src/main/webapp/app/admin/user-management/service/user-management.service.spec.ts @@ -0,0 +1,67 @@ +import { TestBed } from '@angular/core/testing'; +import { HttpErrorResponse } from '@angular/common/http'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; + +import { Authority } from 'app/config/authority.constants'; +import { User } from '../user-management.model'; + +import { UserManagementService } from './user-management.service'; + +describe('User Service', () => { + let service: UserManagementService; + let httpMock: HttpTestingController; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + }); + + service = TestBed.inject(UserManagementService); + httpMock = TestBed.inject(HttpTestingController); + }); + + afterEach(() => { + httpMock.verify(); + }); + + describe('Service methods', () => { + it('should return User', () => { + let expectedResult: string | undefined; + + service.find('user').subscribe(received => { + expectedResult = received.login; + }); + + const req = httpMock.expectOne({ method: 'GET' }); + req.flush(new User(123, 'user')); + expect(expectedResult).toEqual('user'); + }); + + it('should return Authorities', () => { + let expectedResult: string[] = []; + + service.authorities().subscribe(authorities => { + expectedResult = authorities; + }); + const req = httpMock.expectOne({ method: 'GET' }); + + req.flush([Authority.USER, Authority.ADMIN]); + expect(expectedResult).toEqual([Authority.USER, Authority.ADMIN]); + }); + + it('should propagate not found response', () => { + let expectedResult = 0; + + service.find('user').subscribe({ + error: (error: HttpErrorResponse) => (expectedResult = error.status), + }); + + const req = httpMock.expectOne({ method: 'GET' }); + req.flush('Invalid request parameters', { + status: 404, + statusText: 'Bad Request', + }); + expect(expectedResult).toEqual(404); + }); + }); +}); diff --git a/src/main/webapp/app/admin/user-management/service/user-management.service.ts b/src/main/webapp/app/admin/user-management/service/user-management.service.ts new file mode 100644 index 00000000..3c9a8426 --- /dev/null +++ b/src/main/webapp/app/admin/user-management/service/user-management.service.ts @@ -0,0 +1,40 @@ +import { Injectable } from '@angular/core'; +import { HttpClient, HttpResponse } from '@angular/common/http'; +import { Observable } from 'rxjs'; + +import { ApplicationConfigService } from 'app/core/config/application-config.service'; +import { createRequestOption } from 'app/core/request/request-util'; +import { Pagination } from 'app/core/request/request.model'; +import { IUser } from '../user-management.model'; + +@Injectable({ providedIn: 'root' }) +export class UserManagementService { + private resourceUrl = this.applicationConfigService.getEndpointFor('api/admin/users'); + + constructor(private http: HttpClient, private applicationConfigService: ApplicationConfigService) {} + + create(user: IUser): Observable { + return this.http.post(this.resourceUrl, user); + } + + update(user: IUser): Observable { + return this.http.put(this.resourceUrl, user); + } + + find(login: string): Observable { + return this.http.get(`${this.resourceUrl}/${login}`); + } + + query(req?: Pagination): Observable> { + const options = createRequestOption(req); + return this.http.get(this.resourceUrl, { params: options, observe: 'response' }); + } + + delete(login: string): Observable<{}> { + return this.http.delete(`${this.resourceUrl}/${login}`); + } + + authorities(): Observable { + return this.http.get(this.applicationConfigService.getEndpointFor('api/authorities')); + } +} diff --git a/src/main/webapp/app/admin/user-management/update/user-management-update.component.html b/src/main/webapp/app/admin/user-management/update/user-management-update.component.html new file mode 100644 index 00000000..147040f1 --- /dev/null +++ b/src/main/webapp/app/admin/user-management/update/user-management-update.component.html @@ -0,0 +1,142 @@ +
+
+
+

Create or edit a user

+ + + +
+ + +
+ +
+ + + +
+ + This field is required. + + + + This field cannot be longer than 50 characters. + + + + This field can only contain letters, digits and e-mail addresses. + +
+
+ +
+ + + +
+ + This field cannot be longer than 50 characters. + +
+
+ +
+ + + +
+ + This field cannot be longer than 50 characters. + +
+
+ +
+ + + +
+ + This field is required. + + + + This field cannot be longer than 100 characters. + + + + This field is required to be at least 5 characters. + + + + Your email is invalid. + +
+
+ +
+ +
+ +
+ + +
+ +
+ + +
+ + + +
+
+
diff --git a/src/main/webapp/app/admin/user-management/update/user-management-update.component.spec.ts b/src/main/webapp/app/admin/user-management/update/user-management-update.component.spec.ts new file mode 100644 index 00000000..a1d2c27a --- /dev/null +++ b/src/main/webapp/app/admin/user-management/update/user-management-update.component.spec.ts @@ -0,0 +1,95 @@ +import { ComponentFixture, TestBed, waitForAsync, inject, fakeAsync, tick } from '@angular/core/testing'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { FormBuilder } from '@angular/forms'; +import { ActivatedRoute } from '@angular/router'; +import { of } from 'rxjs'; + +import { Authority } from 'app/config/authority.constants'; +import { UserManagementService } from '../service/user-management.service'; +import { User } from '../user-management.model'; + +import { UserManagementUpdateComponent } from './user-management-update.component'; + +describe('User Management Update Component', () => { + let comp: UserManagementUpdateComponent; + let fixture: ComponentFixture; + let service: UserManagementService; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + declarations: [UserManagementUpdateComponent], + providers: [ + FormBuilder, + { + provide: ActivatedRoute, + useValue: { + data: of({ user: new User(123, 'user', 'first', 'last', 'first@last.com', true, 'en', [Authority.USER], 'admin') }), + }, + }, + ], + }) + .overrideTemplate(UserManagementUpdateComponent, '') + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(UserManagementUpdateComponent); + comp = fixture.componentInstance; + service = TestBed.inject(UserManagementService); + }); + + describe('OnInit', () => { + it('Should load authorities and language on init', inject( + [], + fakeAsync(() => { + // GIVEN + jest.spyOn(service, 'authorities').mockReturnValue(of(['USER'])); + + // WHEN + comp.ngOnInit(); + + // THEN + expect(service.authorities).toHaveBeenCalled(); + expect(comp.authorities).toEqual(['USER']); + }) + )); + }); + + describe('save', () => { + it('Should call update service on save for existing user', inject( + [], + fakeAsync(() => { + // GIVEN + const entity = { id: 123 }; + jest.spyOn(service, 'update').mockReturnValue(of(entity)); + comp.editForm.patchValue(entity); + // WHEN + comp.save(); + tick(); // simulate async + + // THEN + expect(service.update).toHaveBeenCalledWith(expect.objectContaining(entity)); + expect(comp.isSaving).toEqual(false); + }) + )); + + it('Should call create service on save for new user', inject( + [], + fakeAsync(() => { + // GIVEN + const entity = { login: 'foo' } as User; + jest.spyOn(service, 'create').mockReturnValue(of(entity)); + comp.editForm.patchValue(entity); + // WHEN + comp.save(); + tick(); // simulate async + + // THEN + expect(comp.editForm.getRawValue().id).toBeNull(); + expect(service.create).toHaveBeenCalledWith(expect.objectContaining(entity)); + expect(comp.isSaving).toEqual(false); + }) + )); + }); +}); diff --git a/src/main/webapp/app/admin/user-management/update/user-management-update.component.ts b/src/main/webapp/app/admin/user-management/update/user-management-update.component.ts new file mode 100644 index 00000000..dfb9fa26 --- /dev/null +++ b/src/main/webapp/app/admin/user-management/update/user-management-update.component.ts @@ -0,0 +1,88 @@ +import { Component, OnInit } from '@angular/core'; +import { FormGroup, FormControl, Validators } from '@angular/forms'; +import { ActivatedRoute } from '@angular/router'; + +import { LANGUAGES } from 'app/config/language.constants'; +import { IUser } from '../user-management.model'; +import { UserManagementService } from '../service/user-management.service'; + +const userTemplate = {} as IUser; + +const newUser: IUser = { + langKey: 'en', + activated: true, +} as IUser; + +@Component({ + selector: 'jhi-user-mgmt-update', + templateUrl: './user-management-update.component.html', +}) +export class UserManagementUpdateComponent implements OnInit { + languages = LANGUAGES; + authorities: string[] = []; + isSaving = false; + + editForm = new FormGroup({ + id: new FormControl(userTemplate.id), + login: new FormControl(userTemplate.login, { + nonNullable: true, + validators: [ + Validators.required, + Validators.minLength(1), + Validators.maxLength(50), + Validators.pattern('^[a-zA-Z0-9!$&*+=?^_`{|}~.-]+@[a-zA-Z0-9-]+(?:\\.[a-zA-Z0-9-]+)*$|^[_.@A-Za-z0-9-]+$'), + ], + }), + firstName: new FormControl(userTemplate.firstName, { validators: [Validators.maxLength(50)] }), + lastName: new FormControl(userTemplate.lastName, { validators: [Validators.maxLength(50)] }), + email: new FormControl(userTemplate.email, { + nonNullable: true, + validators: [Validators.minLength(5), Validators.maxLength(254), Validators.email], + }), + activated: new FormControl(userTemplate.activated, { nonNullable: true }), + langKey: new FormControl(userTemplate.langKey, { nonNullable: true }), + authorities: new FormControl(userTemplate.authorities, { nonNullable: true }), + }); + + constructor(private userService: UserManagementService, private route: ActivatedRoute) {} + + ngOnInit(): void { + this.route.data.subscribe(({ user }) => { + if (user) { + this.editForm.reset(user); + } else { + this.editForm.reset(newUser); + } + }); + this.userService.authorities().subscribe(authorities => (this.authorities = authorities)); + } + + previousState(): void { + window.history.back(); + } + + save(): void { + this.isSaving = true; + const user = this.editForm.getRawValue(); + if (user.id !== null) { + this.userService.update(user).subscribe({ + next: () => this.onSaveSuccess(), + error: () => this.onSaveError(), + }); + } else { + this.userService.create(user).subscribe({ + next: () => this.onSaveSuccess(), + error: () => this.onSaveError(), + }); + } + } + + private onSaveSuccess(): void { + this.isSaving = false; + this.previousState(); + } + + private onSaveError(): void { + this.isSaving = false; + } +} diff --git a/src/main/webapp/app/admin/user-management/user-management.model.ts b/src/main/webapp/app/admin/user-management/user-management.model.ts new file mode 100644 index 00000000..01684285 --- /dev/null +++ b/src/main/webapp/app/admin/user-management/user-management.model.ts @@ -0,0 +1,31 @@ +export interface IUser { + id: number | null; + login?: string; + firstName?: string | null; + lastName?: string | null; + email?: string; + activated?: boolean; + langKey?: string; + authorities?: string[]; + createdBy?: string; + createdDate?: Date; + lastModifiedBy?: string; + lastModifiedDate?: Date; +} + +export class User implements IUser { + constructor( + public id: number | null, + public login?: string, + public firstName?: string | null, + public lastName?: string | null, + public email?: string, + public activated?: boolean, + public langKey?: string, + public authorities?: string[], + public createdBy?: string, + public createdDate?: Date, + public lastModifiedBy?: string, + public lastModifiedDate?: Date + ) {} +} diff --git a/src/main/webapp/app/admin/user-management/user-management.module.ts b/src/main/webapp/app/admin/user-management/user-management.module.ts new file mode 100644 index 00000000..20aa9913 --- /dev/null +++ b/src/main/webapp/app/admin/user-management/user-management.module.ts @@ -0,0 +1,20 @@ +import { NgModule } from '@angular/core'; +import { RouterModule } from '@angular/router'; + +import { SharedModule } from 'app/shared/shared.module'; +import { UserManagementComponent } from './list/user-management.component'; +import { UserManagementDetailComponent } from './detail/user-management-detail.component'; +import { UserManagementUpdateComponent } from './update/user-management-update.component'; +import { UserManagementDeleteDialogComponent } from './delete/user-management-delete-dialog.component'; +import { userManagementRoute } from './user-management.route'; + +@NgModule({ + imports: [SharedModule, RouterModule.forChild(userManagementRoute)], + declarations: [ + UserManagementComponent, + UserManagementDetailComponent, + UserManagementUpdateComponent, + UserManagementDeleteDialogComponent, + ], +}) +export class UserManagementModule {} diff --git a/src/main/webapp/app/admin/user-management/user-management.route.ts b/src/main/webapp/app/admin/user-management/user-management.route.ts new file mode 100644 index 00000000..c8c878b3 --- /dev/null +++ b/src/main/webapp/app/admin/user-management/user-management.route.ts @@ -0,0 +1,53 @@ +import { Injectable } from '@angular/core'; +import { Resolve, ActivatedRouteSnapshot, Routes } from '@angular/router'; +import { Observable, of } from 'rxjs'; + +import { IUser } from './user-management.model'; +import { UserManagementService } from './service/user-management.service'; +import { UserManagementComponent } from './list/user-management.component'; +import { UserManagementDetailComponent } from './detail/user-management-detail.component'; +import { UserManagementUpdateComponent } from './update/user-management-update.component'; + +@Injectable({ providedIn: 'root' }) +export class UserManagementResolve implements Resolve { + constructor(private service: UserManagementService) {} + + resolve(route: ActivatedRouteSnapshot): Observable { + const id = route.params['login']; + if (id) { + return this.service.find(id); + } + return of(null); + } +} + +export const userManagementRoute: Routes = [ + { + path: '', + component: UserManagementComponent, + data: { + defaultSort: 'id,asc', + }, + }, + { + path: ':login/view', + component: UserManagementDetailComponent, + resolve: { + user: UserManagementResolve, + }, + }, + { + path: 'new', + component: UserManagementUpdateComponent, + resolve: { + user: UserManagementResolve, + }, + }, + { + path: ':login/edit', + component: UserManagementUpdateComponent, + resolve: { + user: UserManagementResolve, + }, + }, +]; diff --git a/src/main/webapp/app/app-routing.module.ts b/src/main/webapp/app/app-routing.module.ts new file mode 100644 index 00000000..6b71fc75 --- /dev/null +++ b/src/main/webapp/app/app-routing.module.ts @@ -0,0 +1,43 @@ +import { NgModule } from '@angular/core'; +import { RouterModule } from '@angular/router'; + +import { errorRoute } from './layouts/error/error.route'; +import { navbarRoute } from './layouts/navbar/navbar.route'; +import { DEBUG_INFO_ENABLED } from 'app/app.constants'; +import { Authority } from 'app/config/authority.constants'; + +import { UserRouteAccessService } from 'app/core/auth/user-route-access.service'; + +@NgModule({ + imports: [ + RouterModule.forRoot( + [ + { + path: 'admin', + data: { + authorities: [Authority.ADMIN], + }, + canActivate: [UserRouteAccessService], + loadChildren: () => import('./admin/admin-routing.module').then(m => m.AdminRoutingModule), + }, + { + path: 'account', + loadChildren: () => import('./account/account.module').then(m => m.AccountModule), + }, + { + path: 'login', + loadChildren: () => import('./login/login.module').then(m => m.LoginModule), + }, + { + path: '', + loadChildren: () => import(`./entities/entity-routing.module`).then(m => m.EntityRoutingModule), + }, + navbarRoute, + ...errorRoute, + ], + { enableTracing: DEBUG_INFO_ENABLED } + ), + ], + exports: [RouterModule], +}) +export class AppRoutingModule {} diff --git a/src/main/webapp/app/app.constants.ts b/src/main/webapp/app/app.constants.ts new file mode 100644 index 00000000..695015c4 --- /dev/null +++ b/src/main/webapp/app/app.constants.ts @@ -0,0 +1,9 @@ +// These constants are injected via webpack DefinePlugin variables. +// You can add more variables in webpack.common.js or in profile specific webpack..js files. +// If you change the values in the webpack config files, you need to re run webpack to update the application + +declare const __DEBUG_INFO_ENABLED__: boolean; +declare const __VERSION__: string; + +export const VERSION = __VERSION__; +export const DEBUG_INFO_ENABLED = __DEBUG_INFO_ENABLED__; diff --git a/src/main/webapp/app/app.module.ts b/src/main/webapp/app/app.module.ts new file mode 100644 index 00000000..b2107ee6 --- /dev/null +++ b/src/main/webapp/app/app.module.ts @@ -0,0 +1,58 @@ +import { NgModule, LOCALE_ID } from '@angular/core'; +import { registerLocaleData } from '@angular/common'; +import { HttpClientModule } from '@angular/common/http'; +import locale from '@angular/common/locales/en'; +import { BrowserModule, Title } from '@angular/platform-browser'; +import { ServiceWorkerModule } from '@angular/service-worker'; +import { FaIconLibrary } from '@fortawesome/angular-fontawesome'; +import { NgxWebstorageModule } from 'ngx-webstorage'; +import dayjs from 'dayjs/esm'; +import { NgbDateAdapter, NgbDatepickerConfig } from '@ng-bootstrap/ng-bootstrap'; + +import { ApplicationConfigService } from 'app/core/config/application-config.service'; +import './config/dayjs'; +import { SharedModule } from 'app/shared/shared.module'; +import { TranslationModule } from 'app/shared/language/translation.module'; +import { AppRoutingModule } from './app-routing.module'; +import { HomeModule } from './home/home.module'; +// jhipster-needle-angular-add-module-import JHipster will add new module here +import { NgbDateDayjsAdapter } from './config/datepicker-adapter'; +import { fontAwesomeIcons } from './config/font-awesome-icons'; +import { httpInterceptorProviders } from 'app/core/interceptor/index'; +import { MainComponent } from './layouts/main/main.component'; +import { NavbarComponent } from './layouts/navbar/navbar.component'; +import { FooterComponent } from './layouts/footer/footer.component'; +import { PageRibbonComponent } from './layouts/profiles/page-ribbon.component'; +import { ActiveMenuDirective } from './layouts/navbar/active-menu.directive'; +import { ErrorComponent } from './layouts/error/error.component'; + +@NgModule({ + imports: [ + BrowserModule, + SharedModule, + HomeModule, + // jhipster-needle-angular-add-module JHipster will add new module here + AppRoutingModule, + // Set this to true to enable service worker (PWA) + ServiceWorkerModule.register('ngsw-worker.js', { enabled: false }), + HttpClientModule, + NgxWebstorageModule.forRoot({ prefix: 'jhi', separator: '-', caseSensitive: true }), + TranslationModule, + ], + providers: [ + Title, + { provide: LOCALE_ID, useValue: 'en' }, + { provide: NgbDateAdapter, useClass: NgbDateDayjsAdapter }, + httpInterceptorProviders, + ], + declarations: [MainComponent, NavbarComponent, ErrorComponent, PageRibbonComponent, ActiveMenuDirective, FooterComponent], + bootstrap: [MainComponent], +}) +export class AppModule { + constructor(applicationConfigService: ApplicationConfigService, iconLibrary: FaIconLibrary, dpConfig: NgbDatepickerConfig) { + applicationConfigService.setEndpointPrefix(SERVER_API_URL); + registerLocaleData(locale); + iconLibrary.addIcons(...fontAwesomeIcons); + dpConfig.minDate = { year: dayjs().subtract(100, 'year').year(), month: 1, day: 1 }; + } +} diff --git a/src/main/webapp/app/config/authority.constants.ts b/src/main/webapp/app/config/authority.constants.ts new file mode 100644 index 00000000..1501bcf4 --- /dev/null +++ b/src/main/webapp/app/config/authority.constants.ts @@ -0,0 +1,4 @@ +export enum Authority { + ADMIN = 'ROLE_ADMIN', + USER = 'ROLE_USER', +} diff --git a/src/main/webapp/app/config/datepicker-adapter.ts b/src/main/webapp/app/config/datepicker-adapter.ts new file mode 100644 index 00000000..3f8b16c6 --- /dev/null +++ b/src/main/webapp/app/config/datepicker-adapter.ts @@ -0,0 +1,20 @@ +/** + * Angular bootstrap Date adapter + */ +import { Injectable } from '@angular/core'; +import { NgbDateAdapter, NgbDateStruct } from '@ng-bootstrap/ng-bootstrap'; +import dayjs from 'dayjs/esm'; + +@Injectable() +export class NgbDateDayjsAdapter extends NgbDateAdapter { + fromModel(date: dayjs.Dayjs | null): NgbDateStruct | null { + if (date && dayjs.isDayjs(date) && date.isValid()) { + return { year: date.year(), month: date.month() + 1, day: date.date() }; + } + return null; + } + + toModel(date: NgbDateStruct | null): dayjs.Dayjs | null { + return date ? dayjs(`${date.year}-${date.month}-${date.day}`) : null; + } +} diff --git a/src/main/webapp/app/config/dayjs.ts b/src/main/webapp/app/config/dayjs.ts new file mode 100644 index 00000000..cecb74c2 --- /dev/null +++ b/src/main/webapp/app/config/dayjs.ts @@ -0,0 +1,13 @@ +import dayjs from 'dayjs/esm'; +import customParseFormat from 'dayjs/esm/plugin/customParseFormat'; +import duration from 'dayjs/esm/plugin/duration'; +import relativeTime from 'dayjs/esm/plugin/relativeTime'; + +// jhipster-needle-i18n-language-dayjs-imports - JHipster will import languages from dayjs here +import 'dayjs/esm/locale/en'; +import 'dayjs/esm/locale/fr'; + +// DAYJS CONFIGURATION +dayjs.extend(customParseFormat); +dayjs.extend(duration); +dayjs.extend(relativeTime); diff --git a/src/main/webapp/app/config/error.constants.ts b/src/main/webapp/app/config/error.constants.ts new file mode 100644 index 00000000..eff19a30 --- /dev/null +++ b/src/main/webapp/app/config/error.constants.ts @@ -0,0 +1,3 @@ +export const PROBLEM_BASE_URL = 'https://www.jhipster.tech/problem'; +export const EMAIL_ALREADY_USED_TYPE = `${PROBLEM_BASE_URL}/email-already-used`; +export const LOGIN_ALREADY_USED_TYPE = `${PROBLEM_BASE_URL}/login-already-used`; diff --git a/src/main/webapp/app/config/font-awesome-icons.ts b/src/main/webapp/app/config/font-awesome-icons.ts new file mode 100644 index 00000000..7fcf1696 --- /dev/null +++ b/src/main/webapp/app/config/font-awesome-icons.ts @@ -0,0 +1,83 @@ +import { + faArrowLeft, + faAsterisk, + faBan, + faBars, + faBell, + faBook, + faCalendarAlt, + faCheck, + faCloud, + faCogs, + faDatabase, + faEye, + faFlag, + faHeart, + faHome, + faList, + faLock, + faPencilAlt, + faPlus, + faRoad, + faSave, + faSearch, + faSignOutAlt, + faSignInAlt, + faSort, + faSortDown, + faSortUp, + faSync, + faTachometerAlt, + faTasks, + faThList, + faTimes, + faTrashAlt, + faUser, + faUserPlus, + faUsers, + faUsersCog, + faWrench, + // jhipster-needle-add-icon-import +} from '@fortawesome/free-solid-svg-icons'; + +export const fontAwesomeIcons = [ + faArrowLeft, + faAsterisk, + faBan, + faBars, + faBell, + faBook, + faCalendarAlt, + faCheck, + faCloud, + faCogs, + faDatabase, + faEye, + faFlag, + faHeart, + faHome, + faList, + faLock, + faPencilAlt, + faPlus, + faRoad, + faSave, + faSearch, + faSignOutAlt, + faSignInAlt, + faSort, + faSortDown, + faSortUp, + faSync, + faTachometerAlt, + faTasks, + faThList, + faTimes, + faTrashAlt, + faUser, + faUserPlus, + faUsers, + faUsersCog, + faWrench, + // jhipster-needle-add-icon-import +]; diff --git a/src/main/webapp/app/config/input.constants.ts b/src/main/webapp/app/config/input.constants.ts new file mode 100644 index 00000000..1e3978a9 --- /dev/null +++ b/src/main/webapp/app/config/input.constants.ts @@ -0,0 +1,2 @@ +export const DATE_FORMAT = 'YYYY-MM-DD'; +export const DATE_TIME_FORMAT = 'YYYY-MM-DDTHH:mm'; diff --git a/src/main/webapp/app/config/language.constants.ts b/src/main/webapp/app/config/language.constants.ts new file mode 100644 index 00000000..0679ebb8 --- /dev/null +++ b/src/main/webapp/app/config/language.constants.ts @@ -0,0 +1,9 @@ +/* + Languages codes are ISO_639-1 codes, see http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes + They are written in English to avoid character encoding issues (not a perfect solution) +*/ +export const LANGUAGES: string[] = [ + 'en', + 'fr', + // jhipster-needle-i18n-language-constant - JHipster will add/remove languages in this array +]; diff --git a/src/main/webapp/app/config/navigation.constants.ts b/src/main/webapp/app/config/navigation.constants.ts new file mode 100644 index 00000000..609160d1 --- /dev/null +++ b/src/main/webapp/app/config/navigation.constants.ts @@ -0,0 +1,5 @@ +export const ASC = 'asc'; +export const DESC = 'desc'; +export const SORT = 'sort'; +export const ITEM_DELETED_EVENT = 'deleted'; +export const DEFAULT_SORT_DATA = 'defaultSort'; diff --git a/src/main/webapp/app/config/pagination.constants.ts b/src/main/webapp/app/config/pagination.constants.ts new file mode 100644 index 00000000..6bee3ff5 --- /dev/null +++ b/src/main/webapp/app/config/pagination.constants.ts @@ -0,0 +1,3 @@ +export const TOTAL_COUNT_RESPONSE_HEADER = 'X-Total-Count'; +export const PAGE_HEADER = 'page'; +export const ITEMS_PER_PAGE = 20; diff --git a/src/main/webapp/app/config/translation.config.ts b/src/main/webapp/app/config/translation.config.ts new file mode 100644 index 00000000..b5ac1297 --- /dev/null +++ b/src/main/webapp/app/config/translation.config.ts @@ -0,0 +1,20 @@ +import { HttpClient } from '@angular/common/http'; +import { MissingTranslationHandler, MissingTranslationHandlerParams, TranslateLoader } from '@ngx-translate/core'; +import { TranslateHttpLoader } from '@ngx-translate/http-loader'; + +export const translationNotFoundMessage = 'translation-not-found'; + +export class MissingTranslationHandlerImpl implements MissingTranslationHandler { + handle(params: MissingTranslationHandlerParams): string { + const key = params.key; + return `${translationNotFoundMessage}[${key}]`; + } +} + +export function translatePartialLoader(http: HttpClient): TranslateLoader { + return new TranslateHttpLoader(http, 'i18n/', `.json?_=${I18N_HASH}`); +} + +export function missingTranslationHandler(): MissingTranslationHandler { + return new MissingTranslationHandlerImpl(); +} diff --git a/src/main/webapp/app/config/uib-pagination.config.ts b/src/main/webapp/app/config/uib-pagination.config.ts new file mode 100644 index 00000000..ecabe165 --- /dev/null +++ b/src/main/webapp/app/config/uib-pagination.config.ts @@ -0,0 +1,14 @@ +import { Injectable } from '@angular/core'; +import { NgbPaginationConfig } from '@ng-bootstrap/ng-bootstrap'; + +import { ITEMS_PER_PAGE } from 'app/config/pagination.constants'; + +@Injectable({ providedIn: 'root' }) +export class PaginationConfig { + constructor(config: NgbPaginationConfig) { + config.boundaryLinks = true; + config.maxSize = 5; + config.pageSize = ITEMS_PER_PAGE; + config.size = 'sm'; + } +} diff --git a/src/main/webapp/app/core/auth/account.model.ts b/src/main/webapp/app/core/auth/account.model.ts new file mode 100644 index 00000000..22e083cf --- /dev/null +++ b/src/main/webapp/app/core/auth/account.model.ts @@ -0,0 +1,12 @@ +export class Account { + constructor( + public activated: boolean, + public authorities: string[], + public email: string, + public firstName: string | null, + public langKey: string, + public lastName: string | null, + public login: string, + public imageUrl: string | null + ) {} +} diff --git a/src/main/webapp/app/core/auth/account.service.spec.ts b/src/main/webapp/app/core/auth/account.service.spec.ts new file mode 100644 index 00000000..1213dab6 --- /dev/null +++ b/src/main/webapp/app/core/auth/account.service.spec.ts @@ -0,0 +1,251 @@ +jest.mock('app/core/auth/state-storage.service'); + +import { Router } from '@angular/router'; +import { RouterTestingModule } from '@angular/router/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; +import { TestBed } from '@angular/core/testing'; +import { TranslateModule, TranslateService } from '@ngx-translate/core'; +import { of } from 'rxjs'; +import { NgxWebstorageModule, SessionStorageService } from 'ngx-webstorage'; + +import { Account } from 'app/core/auth/account.model'; +import { Authority } from 'app/config/authority.constants'; +import { StateStorageService } from 'app/core/auth/state-storage.service'; +import { ApplicationConfigService } from 'app/core/config/application-config.service'; + +import { AccountService } from './account.service'; + +function accountWithAuthorities(authorities: string[]): Account { + return { + activated: true, + authorities, + email: '', + firstName: '', + langKey: '', + lastName: '', + login: '', + imageUrl: '', + }; +} + +describe('Account Service', () => { + let service: AccountService; + let applicationConfigService: ApplicationConfigService; + let httpMock: HttpTestingController; + let mockStorageService: StateStorageService; + let mockRouter: Router; + let mockTranslateService: TranslateService; + let sessionStorageService: SessionStorageService; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgxWebstorageModule.forRoot()], + providers: [StateStorageService], + }); + + service = TestBed.inject(AccountService); + applicationConfigService = TestBed.inject(ApplicationConfigService); + httpMock = TestBed.inject(HttpTestingController); + mockStorageService = TestBed.inject(StateStorageService); + mockRouter = TestBed.inject(Router); + jest.spyOn(mockRouter, 'navigateByUrl').mockImplementation(() => Promise.resolve(true)); + + mockTranslateService = TestBed.inject(TranslateService); + jest.spyOn(mockTranslateService, 'use').mockImplementation(() => of('')); + sessionStorageService = TestBed.inject(SessionStorageService); + }); + + afterEach(() => { + httpMock.verify(); + }); + + describe('save', () => { + it('should call account saving endpoint with correct values', () => { + // GIVEN + const account = accountWithAuthorities([]); + + // WHEN + service.save(account).subscribe(); + const testRequest = httpMock.expectOne({ method: 'POST', url: applicationConfigService.getEndpointFor('api/account') }); + testRequest.flush({}); + + // THEN + expect(testRequest.request.body).toEqual(account); + }); + }); + + describe('authenticate', () => { + it('authenticationState should emit null if input is null', () => { + // GIVEN + let userIdentity: Account | null = accountWithAuthorities([]); + service.getAuthenticationState().subscribe(account => (userIdentity = account)); + + // WHEN + service.authenticate(null); + + // THEN + expect(userIdentity).toBeNull(); + expect(service.isAuthenticated()).toBe(false); + }); + + it('authenticationState should emit the same account as was in input parameter', () => { + // GIVEN + const expectedResult = accountWithAuthorities([]); + let userIdentity: Account | null = null; + service.getAuthenticationState().subscribe(account => (userIdentity = account)); + + // WHEN + service.authenticate(expectedResult); + + // THEN + expect(userIdentity).toEqual(expectedResult); + expect(service.isAuthenticated()).toBe(true); + }); + }); + + describe('identity', () => { + it('should call /account only once if last call have not returned', () => { + // When I call + service.identity().subscribe(); + // Once more + service.identity().subscribe(); + // Then there is only request + httpMock.expectOne({ method: 'GET' }); + }); + + it('should call /account only once if not logged out after first authentication and should call /account again if user has logged out', () => { + // Given the user is authenticated + service.identity().subscribe(); + httpMock.expectOne({ method: 'GET' }).flush({}); + + // When I call + service.identity().subscribe(); + + // Then there is no second request + httpMock.expectNone({ method: 'GET' }); + + // When I log out + service.authenticate(null); + // and then call + service.identity().subscribe(); + + // Then there is a new request + httpMock.expectOne({ method: 'GET' }); + }); + + describe('should change the language on authentication if necessary', () => { + it('should change language if user has not changed language manually', () => { + // GIVEN + sessionStorageService.retrieve = jest.fn(key => (key === 'locale' ? undefined : 'otherSessionStorageValue')); + + // WHEN + service.identity().subscribe(); + httpMock.expectOne({ method: 'GET' }).flush({ ...accountWithAuthorities([]), langKey: 'accountLang' }); + + // THEN + expect(mockTranslateService.use).toHaveBeenCalledWith('accountLang'); + }); + + it('should not change language if user has changed language manually', () => { + // GIVEN + sessionStorageService.retrieve = jest.fn(key => (key === 'locale' ? 'sessionLang' : undefined)); + + // WHEN + service.identity().subscribe(); + httpMock.expectOne({ method: 'GET' }).flush({ ...accountWithAuthorities([]), langKey: 'accountLang' }); + + // THEN + expect(mockTranslateService.use).not.toHaveBeenCalled(); + }); + }); + + describe('navigateToStoredUrl', () => { + it('should navigate to the previous stored url post successful authentication', () => { + // GIVEN + mockStorageService.getUrl = jest.fn(() => 'admin/users?page=0'); + + // WHEN + service.identity().subscribe(); + httpMock.expectOne({ method: 'GET' }).flush({}); + + // THEN + expect(mockStorageService.getUrl).toHaveBeenCalledTimes(1); + expect(mockStorageService.clearUrl).toHaveBeenCalledTimes(1); + expect(mockRouter.navigateByUrl).toHaveBeenCalledWith('admin/users?page=0'); + }); + + it('should not navigate to the previous stored url when authentication fails', () => { + // WHEN + service.identity().subscribe(); + httpMock.expectOne({ method: 'GET' }).error(new ErrorEvent('')); + + // THEN + expect(mockStorageService.getUrl).not.toHaveBeenCalled(); + expect(mockStorageService.clearUrl).not.toHaveBeenCalled(); + expect(mockRouter.navigateByUrl).not.toHaveBeenCalled(); + }); + + it('should not navigate to the previous stored url when no such url exists post successful authentication', () => { + // GIVEN + mockStorageService.getUrl = jest.fn(() => null); + + // WHEN + service.identity().subscribe(); + httpMock.expectOne({ method: 'GET' }).flush({}); + + // THEN + expect(mockStorageService.getUrl).toHaveBeenCalledTimes(1); + expect(mockStorageService.clearUrl).not.toHaveBeenCalled(); + expect(mockRouter.navigateByUrl).not.toHaveBeenCalled(); + }); + }); + }); + + describe('hasAnyAuthority', () => { + describe('hasAnyAuthority string parameter', () => { + it('should return false if user is not logged', () => { + const hasAuthority = service.hasAnyAuthority(Authority.USER); + expect(hasAuthority).toBe(false); + }); + + it('should return false if user is logged and has not authority', () => { + service.authenticate(accountWithAuthorities([Authority.USER])); + + const hasAuthority = service.hasAnyAuthority(Authority.ADMIN); + + expect(hasAuthority).toBe(false); + }); + + it('should return true if user is logged and has authority', () => { + service.authenticate(accountWithAuthorities([Authority.USER])); + + const hasAuthority = service.hasAnyAuthority(Authority.USER); + + expect(hasAuthority).toBe(true); + }); + }); + + describe('hasAnyAuthority array parameter', () => { + it('should return false if user is not logged', () => { + const hasAuthority = service.hasAnyAuthority([Authority.USER]); + expect(hasAuthority).toBeFalsy(); + }); + + it('should return false if user is logged and has not authority', () => { + service.authenticate(accountWithAuthorities([Authority.USER])); + + const hasAuthority = service.hasAnyAuthority([Authority.ADMIN]); + + expect(hasAuthority).toBe(false); + }); + + it('should return true if user is logged and has authority', () => { + service.authenticate(accountWithAuthorities([Authority.USER])); + + const hasAuthority = service.hasAnyAuthority([Authority.USER, Authority.ADMIN]); + + expect(hasAuthority).toBe(true); + }); + }); + }); +}); diff --git a/src/main/webapp/app/core/auth/account.service.ts b/src/main/webapp/app/core/auth/account.service.ts new file mode 100644 index 00000000..441d69aa --- /dev/null +++ b/src/main/webapp/app/core/auth/account.service.ts @@ -0,0 +1,92 @@ +import { Injectable } from '@angular/core'; +import { Router } from '@angular/router'; +import { HttpClient } from '@angular/common/http'; +import { TranslateService } from '@ngx-translate/core'; +import { SessionStorageService } from 'ngx-webstorage'; +import { Observable, ReplaySubject, of } from 'rxjs'; +import { shareReplay, tap, catchError } from 'rxjs/operators'; + +import { StateStorageService } from 'app/core/auth/state-storage.service'; +import { ApplicationConfigService } from '../config/application-config.service'; +import { Account } from 'app/core/auth/account.model'; + +@Injectable({ providedIn: 'root' }) +export class AccountService { + private userIdentity: Account | null = null; + private authenticationState = new ReplaySubject(1); + private accountCache$?: Observable | null; + + constructor( + private translateService: TranslateService, + private sessionStorageService: SessionStorageService, + private http: HttpClient, + private stateStorageService: StateStorageService, + private router: Router, + private applicationConfigService: ApplicationConfigService + ) {} + + save(account: Account): Observable<{}> { + return this.http.post(this.applicationConfigService.getEndpointFor('api/account'), account); + } + + authenticate(identity: Account | null): void { + this.userIdentity = identity; + this.authenticationState.next(this.userIdentity); + if (!identity) { + this.accountCache$ = null; + } + } + + hasAnyAuthority(authorities: string[] | string): boolean { + if (!this.userIdentity) { + return false; + } + if (!Array.isArray(authorities)) { + authorities = [authorities]; + } + return this.userIdentity.authorities.some((authority: string) => authorities.includes(authority)); + } + + identity(force?: boolean): Observable { + if (!this.accountCache$ || force) { + this.accountCache$ = this.fetch().pipe( + tap((account: Account) => { + this.authenticate(account); + + // After retrieve the account info, the language will be changed to + // the user's preferred language configured in the account setting + // unless user have choosed other language in the current session + if (!this.sessionStorageService.retrieve('locale')) { + this.translateService.use(account.langKey); + } + + this.navigateToStoredUrl(); + }), + shareReplay() + ); + } + return this.accountCache$.pipe(catchError(() => of(null))); + } + + isAuthenticated(): boolean { + return this.userIdentity !== null; + } + + getAuthenticationState(): Observable { + return this.authenticationState.asObservable(); + } + + private fetch(): Observable { + return this.http.get(this.applicationConfigService.getEndpointFor('api/account')); + } + + private navigateToStoredUrl(): void { + // previousState can be set in the authExpiredInterceptor and in the userRouteAccessService + // if login is successful, go to stored previousState and clear previousState + const previousUrl = this.stateStorageService.getUrl(); + if (previousUrl) { + this.stateStorageService.clearUrl(); + this.router.navigateByUrl(previousUrl); + } + } +} diff --git a/src/main/webapp/app/core/auth/auth-jwt.service.spec.ts b/src/main/webapp/app/core/auth/auth-jwt.service.spec.ts new file mode 100644 index 00000000..c11953c3 --- /dev/null +++ b/src/main/webapp/app/core/auth/auth-jwt.service.spec.ts @@ -0,0 +1,88 @@ +import { TestBed } from '@angular/core/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; +import { AuthServerProvider } from 'app/core/auth/auth-jwt.service'; +import { LocalStorageService, NgxWebstorageModule, SessionStorageService } from 'ngx-webstorage'; + +describe('Auth JWT', () => { + let service: AuthServerProvider; + let localStorageService: LocalStorageService; + let sessionStorageService: SessionStorageService; + let httpMock: HttpTestingController; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule, NgxWebstorageModule.forRoot()], + }); + + httpMock = TestBed.inject(HttpTestingController); + service = TestBed.inject(AuthServerProvider); + localStorageService = TestBed.inject(LocalStorageService); + sessionStorageService = TestBed.inject(SessionStorageService); + }); + + describe('Get Token', () => { + it('should return empty token if not found in local storage nor session storage', () => { + const result = service.getToken(); + expect(result).toEqual(''); + }); + + it('should return token from session storage if local storage is empty', () => { + sessionStorageService.retrieve = jest.fn().mockReturnValue('sessionStorageToken'); + const result = service.getToken(); + expect(result).toEqual('sessionStorageToken'); + }); + + it('should return token from localstorage storage', () => { + localStorageService.retrieve = jest.fn().mockReturnValue('localStorageToken'); + const result = service.getToken(); + expect(result).toEqual('localStorageToken'); + }); + }); + + describe('Login', () => { + it('should clear session storage and save in local storage when rememberMe is true', () => { + // GIVEN + localStorageService.store = jest.fn(); + sessionStorageService.clear = jest.fn(); + + // WHEN + service.login({ username: 'John', password: '123', rememberMe: true }).subscribe(); + httpMock.expectOne('api/authenticate').flush({ id_token: '1' }); + + // THEN + httpMock.verify(); + expect(localStorageService.store).toHaveBeenCalledWith('authenticationToken', '1'); + expect(sessionStorageService.clear).toHaveBeenCalled(); + }); + + it('should clear local storage and save in session storage when rememberMe is false', () => { + // GIVEN + sessionStorageService.store = jest.fn(); + localStorageService.clear = jest.fn(); + + // WHEN + service.login({ username: 'John', password: '123', rememberMe: false }).subscribe(); + httpMock.expectOne('api/authenticate').flush({ id_token: '1' }); + + // THEN + httpMock.verify(); + expect(sessionStorageService.store).toHaveBeenCalledWith('authenticationToken', '1'); + expect(localStorageService.clear).toHaveBeenCalled(); + }); + }); + + describe('Logout', () => { + it('should clear storage', () => { + // GIVEN + sessionStorageService.clear = jest.fn(); + localStorageService.clear = jest.fn(); + + // WHEN + service.logout().subscribe(); + + // THEN + expect(localStorageService.clear).toHaveBeenCalled(); + expect(sessionStorageService.clear).toHaveBeenCalled(); + }); + }); +}); diff --git a/src/main/webapp/app/core/auth/auth-jwt.service.ts b/src/main/webapp/app/core/auth/auth-jwt.service.ts new file mode 100644 index 00000000..2316a189 --- /dev/null +++ b/src/main/webapp/app/core/auth/auth-jwt.service.ts @@ -0,0 +1,53 @@ +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { LocalStorageService, SessionStorageService } from 'ngx-webstorage'; + +import { ApplicationConfigService } from '../config/application-config.service'; +import { Login } from 'app/login/login.model'; + +type JwtToken = { + id_token: string; +}; + +@Injectable({ providedIn: 'root' }) +export class AuthServerProvider { + constructor( + private http: HttpClient, + private localStorageService: LocalStorageService, + private sessionStorageService: SessionStorageService, + private applicationConfigService: ApplicationConfigService + ) {} + + getToken(): string { + const tokenInLocalStorage: string | null = this.localStorageService.retrieve('authenticationToken'); + const tokenInSessionStorage: string | null = this.sessionStorageService.retrieve('authenticationToken'); + return tokenInLocalStorage ?? tokenInSessionStorage ?? ''; + } + + login(credentials: Login): Observable { + return this.http + .post(this.applicationConfigService.getEndpointFor('api/authenticate'), credentials) + .pipe(map(response => this.authenticateSuccess(response, credentials.rememberMe))); + } + + logout(): Observable { + return new Observable(observer => { + this.localStorageService.clear('authenticationToken'); + this.sessionStorageService.clear('authenticationToken'); + observer.complete(); + }); + } + + private authenticateSuccess(response: JwtToken, rememberMe: boolean): void { + const jwt = response.id_token; + if (rememberMe) { + this.localStorageService.store('authenticationToken', jwt); + this.sessionStorageService.clear('authenticationToken'); + } else { + this.sessionStorageService.store('authenticationToken', jwt); + this.localStorageService.clear('authenticationToken'); + } + } +} diff --git a/src/main/webapp/app/core/auth/state-storage.service.ts b/src/main/webapp/app/core/auth/state-storage.service.ts new file mode 100644 index 00000000..e058d7d3 --- /dev/null +++ b/src/main/webapp/app/core/auth/state-storage.service.ts @@ -0,0 +1,21 @@ +import { Injectable } from '@angular/core'; +import { SessionStorageService } from 'ngx-webstorage'; + +@Injectable({ providedIn: 'root' }) +export class StateStorageService { + private previousUrlKey = 'previousUrl'; + + constructor(private sessionStorageService: SessionStorageService) {} + + storeUrl(url: string): void { + this.sessionStorageService.store(this.previousUrlKey, url); + } + + getUrl(): string | null { + return this.sessionStorageService.retrieve(this.previousUrlKey) as string | null; + } + + clearUrl(): void { + this.sessionStorageService.clear(this.previousUrlKey); + } +} diff --git a/src/main/webapp/app/core/auth/user-route-access.service.ts b/src/main/webapp/app/core/auth/user-route-access.service.ts new file mode 100644 index 00000000..6bb47168 --- /dev/null +++ b/src/main/webapp/app/core/auth/user-route-access.service.ts @@ -0,0 +1,36 @@ +import { Injectable, isDevMode } from '@angular/core'; +import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; + +import { AccountService } from 'app/core/auth/account.service'; +import { StateStorageService } from './state-storage.service'; + +@Injectable({ providedIn: 'root' }) +export class UserRouteAccessService implements CanActivate { + constructor(private router: Router, private accountService: AccountService, private stateStorageService: StateStorageService) {} + + canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable { + return this.accountService.identity().pipe( + map(account => { + if (account) { + const authorities = route.data['authorities']; + + if (!authorities || authorities.length === 0 || this.accountService.hasAnyAuthority(authorities)) { + return true; + } + + if (isDevMode()) { + console.error('User has not any of required authorities: ', authorities); + } + this.router.navigate(['accessdenied']); + return false; + } + + this.stateStorageService.storeUrl(state.url); + this.router.navigate(['/login']); + return false; + }) + ); + } +} diff --git a/src/main/webapp/app/core/config/application-config.service.spec.ts b/src/main/webapp/app/core/config/application-config.service.spec.ts new file mode 100644 index 00000000..4451c9bb --- /dev/null +++ b/src/main/webapp/app/core/config/application-config.service.spec.ts @@ -0,0 +1,40 @@ +import { TestBed } from '@angular/core/testing'; + +import { ApplicationConfigService } from './application-config.service'; + +describe('ApplicationConfigService', () => { + let service: ApplicationConfigService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(ApplicationConfigService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); + + describe('without prefix', () => { + it('should return correctly', () => { + expect(service.getEndpointFor('api')).toEqual('api'); + }); + + it('should return correctly when passing microservice', () => { + expect(service.getEndpointFor('api', 'microservice')).toEqual('services/microservice/api'); + }); + }); + + describe('with prefix', () => { + beforeEach(() => { + service.setEndpointPrefix('prefix/'); + }); + + it('should return correctly', () => { + expect(service.getEndpointFor('api')).toEqual('prefix/api'); + }); + + it('should return correctly when passing microservice', () => { + expect(service.getEndpointFor('api', 'microservice')).toEqual('prefix/services/microservice/api'); + }); + }); +}); diff --git a/src/main/webapp/app/core/config/application-config.service.ts b/src/main/webapp/app/core/config/application-config.service.ts new file mode 100644 index 00000000..0102e5f0 --- /dev/null +++ b/src/main/webapp/app/core/config/application-config.service.ts @@ -0,0 +1,28 @@ +import { Injectable } from '@angular/core'; + +@Injectable({ + providedIn: 'root', +}) +export class ApplicationConfigService { + private endpointPrefix = ''; + private microfrontend = false; + + setEndpointPrefix(endpointPrefix: string): void { + this.endpointPrefix = endpointPrefix; + } + + setMicrofrontend(microfrontend = true): void { + this.microfrontend = microfrontend; + } + + isMicrofrontend(): boolean { + return this.microfrontend; + } + + getEndpointFor(api: string, microservice?: string): string { + if (microservice) { + return `${this.endpointPrefix}services/${microservice}/${api}`; + } + return `${this.endpointPrefix}${api}`; + } +} diff --git a/src/main/webapp/app/core/interceptor/auth-expired.interceptor.ts b/src/main/webapp/app/core/interceptor/auth-expired.interceptor.ts new file mode 100644 index 00000000..bdc8e8a1 --- /dev/null +++ b/src/main/webapp/app/core/interceptor/auth-expired.interceptor.ts @@ -0,0 +1,33 @@ +import { Injectable } from '@angular/core'; +import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpErrorResponse } from '@angular/common/http'; +import { Observable } from 'rxjs'; +import { tap } from 'rxjs/operators'; +import { Router } from '@angular/router'; + +import { LoginService } from 'app/login/login.service'; +import { StateStorageService } from 'app/core/auth/state-storage.service'; +import { AccountService } from 'app/core/auth/account.service'; + +@Injectable() +export class AuthExpiredInterceptor implements HttpInterceptor { + constructor( + private loginService: LoginService, + private stateStorageService: StateStorageService, + private router: Router, + private accountService: AccountService + ) {} + + intercept(request: HttpRequest, next: HttpHandler): Observable> { + return next.handle(request).pipe( + tap({ + error: (err: HttpErrorResponse) => { + if (err.status === 401 && err.url && !err.url.includes('api/account') && this.accountService.isAuthenticated()) { + this.stateStorageService.storeUrl(this.router.routerState.snapshot.url); + this.loginService.logout(); + this.router.navigate(['/login']); + } + }, + }) + ); + } +} diff --git a/src/main/webapp/app/core/interceptor/auth.interceptor.ts b/src/main/webapp/app/core/interceptor/auth.interceptor.ts new file mode 100644 index 00000000..81b639a7 --- /dev/null +++ b/src/main/webapp/app/core/interceptor/auth.interceptor.ts @@ -0,0 +1,33 @@ +import { Injectable } from '@angular/core'; +import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http'; +import { Observable } from 'rxjs'; +import { LocalStorageService, SessionStorageService } from 'ngx-webstorage'; + +import { ApplicationConfigService } from '../config/application-config.service'; + +@Injectable() +export class AuthInterceptor implements HttpInterceptor { + constructor( + private localStorageService: LocalStorageService, + private sessionStorageService: SessionStorageService, + private applicationConfigService: ApplicationConfigService + ) {} + + intercept(request: HttpRequest, next: HttpHandler): Observable> { + const serverApiUrl = this.applicationConfigService.getEndpointFor(''); + if (!request.url || (request.url.startsWith('http') && !(serverApiUrl && request.url.startsWith(serverApiUrl)))) { + return next.handle(request); + } + + const token: string | null = + this.localStorageService.retrieve('authenticationToken') ?? this.sessionStorageService.retrieve('authenticationToken'); + if (token) { + request = request.clone({ + setHeaders: { + Authorization: `Bearer ${token}`, + }, + }); + } + return next.handle(request); + } +} diff --git a/src/main/webapp/app/core/interceptor/error-handler.interceptor.ts b/src/main/webapp/app/core/interceptor/error-handler.interceptor.ts new file mode 100644 index 00000000..a53bb4b3 --- /dev/null +++ b/src/main/webapp/app/core/interceptor/error-handler.interceptor.ts @@ -0,0 +1,23 @@ +import { Injectable } from '@angular/core'; +import { HttpInterceptor, HttpRequest, HttpErrorResponse, HttpHandler, HttpEvent } from '@angular/common/http'; +import { Observable } from 'rxjs'; +import { tap } from 'rxjs/operators'; + +import { EventManager, EventWithContent } from 'app/core/util/event-manager.service'; + +@Injectable() +export class ErrorHandlerInterceptor implements HttpInterceptor { + constructor(private eventManager: EventManager) {} + + intercept(request: HttpRequest, next: HttpHandler): Observable> { + return next.handle(request).pipe( + tap({ + error: (err: HttpErrorResponse) => { + if (!(err.status === 401 && (err.message === '' || err.url?.includes('api/account')))) { + this.eventManager.broadcast(new EventWithContent('twentyOnePointsApp.httpError', err)); + } + }, + }) + ); + } +} diff --git a/src/main/webapp/app/core/interceptor/index.ts b/src/main/webapp/app/core/interceptor/index.ts new file mode 100644 index 00000000..f7e72e3a --- /dev/null +++ b/src/main/webapp/app/core/interceptor/index.ts @@ -0,0 +1,29 @@ +import { HTTP_INTERCEPTORS } from '@angular/common/http'; + +import { AuthInterceptor } from 'app/core/interceptor/auth.interceptor'; +import { AuthExpiredInterceptor } from 'app/core/interceptor/auth-expired.interceptor'; +import { ErrorHandlerInterceptor } from 'app/core/interceptor/error-handler.interceptor'; +import { NotificationInterceptor } from 'app/core/interceptor/notification.interceptor'; + +export const httpInterceptorProviders = [ + { + provide: HTTP_INTERCEPTORS, + useClass: AuthInterceptor, + multi: true, + }, + { + provide: HTTP_INTERCEPTORS, + useClass: AuthExpiredInterceptor, + multi: true, + }, + { + provide: HTTP_INTERCEPTORS, + useClass: ErrorHandlerInterceptor, + multi: true, + }, + { + provide: HTTP_INTERCEPTORS, + useClass: NotificationInterceptor, + multi: true, + }, +]; diff --git a/src/main/webapp/app/core/interceptor/notification.interceptor.ts b/src/main/webapp/app/core/interceptor/notification.interceptor.ts new file mode 100644 index 00000000..513132f9 --- /dev/null +++ b/src/main/webapp/app/core/interceptor/notification.interceptor.ts @@ -0,0 +1,38 @@ +import { HttpInterceptor, HttpRequest, HttpResponse, HttpHandler, HttpEvent } from '@angular/common/http'; +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; +import { tap } from 'rxjs/operators'; + +import { AlertService } from 'app/core/util/alert.service'; + +@Injectable() +export class NotificationInterceptor implements HttpInterceptor { + constructor(private alertService: AlertService) {} + + intercept(request: HttpRequest, next: HttpHandler): Observable> { + return next.handle(request).pipe( + tap((event: HttpEvent) => { + if (event instanceof HttpResponse) { + let alert: string | null = null; + let alertParams: string | null = null; + + for (const headerKey of event.headers.keys()) { + if (headerKey.toLowerCase().endsWith('app-alert')) { + alert = event.headers.get(headerKey); + } else if (headerKey.toLowerCase().endsWith('app-params')) { + alertParams = decodeURIComponent(event.headers.get(headerKey)!.replace(/\+/g, ' ')); + } + } + + if (alert) { + this.alertService.addAlert({ + type: 'success', + translationKey: alert, + translationParams: { param: alertParams }, + }); + } + } + }) + ); + } +} diff --git a/src/main/webapp/app/core/request/request-util.ts b/src/main/webapp/app/core/request/request-util.ts new file mode 100644 index 00000000..38c6663f --- /dev/null +++ b/src/main/webapp/app/core/request/request-util.ts @@ -0,0 +1,23 @@ +import { HttpParams } from '@angular/common/http'; + +export const createRequestOption = (req?: any): HttpParams => { + let options: HttpParams = new HttpParams(); + + if (req) { + Object.keys(req).forEach(key => { + if (key !== 'sort' && req[key]) { + for (const value of [].concat(req[key]).filter(v => v !== '')) { + options = options.append(key, value); + } + } + }); + + if (req.sort) { + req.sort.forEach((val: string) => { + options = options.append('sort', val); + }); + } + } + + return options; +}; diff --git a/src/main/webapp/app/core/request/request.model.ts b/src/main/webapp/app/core/request/request.model.ts new file mode 100644 index 00000000..5de2b69a --- /dev/null +++ b/src/main/webapp/app/core/request/request.model.ts @@ -0,0 +1,11 @@ +export interface Pagination { + page: number; + size: number; + sort: string[]; +} + +export interface Search { + query: string; +} + +export interface SearchWithPagination extends Search, Pagination {} diff --git a/src/main/webapp/app/core/util/alert.service.spec.ts b/src/main/webapp/app/core/util/alert.service.spec.ts new file mode 100644 index 00000000..934188c7 --- /dev/null +++ b/src/main/webapp/app/core/util/alert.service.spec.ts @@ -0,0 +1,285 @@ +import { inject, TestBed } from '@angular/core/testing'; +import { TranslateModule, TranslateService, MissingTranslationHandler } from '@ngx-translate/core'; +import { missingTranslationHandler } from '../../config/translation.config'; + +import { Alert, AlertService } from './alert.service'; + +describe('Alert service test', () => { + describe('Alert Service Test', () => { + let extAlerts: Alert[]; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [ + TranslateModule.forRoot({ + missingTranslationHandler: { + provide: MissingTranslationHandler, + useFactory: missingTranslationHandler, + }, + }), + ], + }); + const translateService = TestBed.inject(TranslateService); + translateService.setDefaultLang('en'); + jest.useFakeTimers(); + extAlerts = []; + }); + + it('should produce a proper alert object and fetch it', inject([AlertService], (service: AlertService) => { + expect( + service.addAlert({ + type: 'success', + message: 'Hello Jhipster', + timeout: 3000, + toast: true, + position: 'top left', + }) + ).toEqual( + expect.objectContaining({ + type: 'success', + message: 'Hello Jhipster', + id: 0, + timeout: 3000, + toast: true, + position: 'top left', + } as Alert) + ); + + expect(service.get().length).toBe(1); + expect(service.get()[0]).toEqual( + expect.objectContaining({ + type: 'success', + message: 'Hello Jhipster', + id: 0, + timeout: 3000, + toast: true, + position: 'top left', + } as Alert) + ); + })); + + it('should produce a proper alert object and add it to external alert objects array', inject( + [AlertService], + (service: AlertService) => { + expect( + service.addAlert( + { + type: 'success', + message: 'Hello Jhipster', + timeout: 3000, + toast: true, + position: 'top left', + }, + extAlerts + ) + ).toEqual( + expect.objectContaining({ + type: 'success', + message: 'Hello Jhipster', + id: 0, + timeout: 3000, + toast: true, + position: 'top left', + } as Alert) + ); + + expect(extAlerts.length).toBe(1); + expect(extAlerts[0]).toEqual( + expect.objectContaining({ + type: 'success', + message: 'Hello Jhipster', + id: 0, + timeout: 3000, + toast: true, + position: 'top left', + } as Alert) + ); + } + )); + + it('should produce an alert object with correct id', inject([AlertService], (service: AlertService) => { + service.addAlert({ type: 'info', message: 'Hello Jhipster info' }); + expect(service.addAlert({ type: 'success', message: 'Hello Jhipster success' })).toEqual( + expect.objectContaining({ + type: 'success', + message: 'Hello Jhipster success', + id: 1, + } as Alert) + ); + + expect(service.get().length).toBe(2); + expect(service.get()[1]).toEqual( + expect.objectContaining({ + type: 'success', + message: 'Hello Jhipster success', + id: 1, + } as Alert) + ); + })); + + it('should close an alert correctly', inject([AlertService], (service: AlertService) => { + const alert0 = service.addAlert({ type: 'info', message: 'Hello Jhipster info' }); + const alert1 = service.addAlert({ type: 'info', message: 'Hello Jhipster info 2' }); + const alert2 = service.addAlert({ type: 'success', message: 'Hello Jhipster success' }); + expect(alert2).toEqual( + expect.objectContaining({ + type: 'success', + message: 'Hello Jhipster success', + id: 2, + } as Alert) + ); + + expect(service.get().length).toBe(3); + alert1.close?.(service.get()); + expect(service.get().length).toBe(2); + expect(service.get()[1]).not.toEqual( + expect.objectContaining({ + type: 'info', + message: 'Hello Jhipster info 2', + id: 1, + } as Alert) + ); + alert2.close?.(service.get()); + expect(service.get().length).toBe(1); + expect(service.get()[0]).not.toEqual( + expect.objectContaining({ + type: 'success', + message: 'Hello Jhipster success', + id: 2, + } as Alert) + ); + alert0.close?.(service.get()); + expect(service.get().length).toBe(0); + })); + + it('should close an alert on timeout correctly', inject([AlertService], (service: AlertService) => { + service.addAlert({ type: 'info', message: 'Hello Jhipster info' }); + + expect(service.get().length).toBe(1); + + jest.advanceTimersByTime(6000); + + expect(service.get().length).toBe(0); + })); + + it('should clear alerts', inject([AlertService], (service: AlertService) => { + service.addAlert({ type: 'info', message: 'Hello Jhipster info' }); + service.addAlert({ type: 'danger', message: 'Hello Jhipster info' }); + service.addAlert({ type: 'success', message: 'Hello Jhipster info' }); + expect(service.get().length).toBe(3); + service.clear(); + expect(service.get().length).toBe(0); + })); + + it('should produce a scoped alert', inject([AlertService], (service: AlertService) => { + expect( + service.addAlert( + { + type: 'success', + message: 'Hello Jhipster', + timeout: 3000, + toast: true, + position: 'top left', + }, + [] + ) + ).toEqual( + expect.objectContaining({ + type: 'success', + message: 'Hello Jhipster', + id: 0, + timeout: 3000, + toast: true, + position: 'top left', + } as Alert) + ); + + expect(service.get().length).toBe(0); + })); + + it('should produce a success message', inject([AlertService], (service: AlertService) => { + expect(service.addAlert({ type: 'success', message: 'Hello Jhipster' })).toEqual( + expect.objectContaining({ + type: 'success', + message: 'Hello Jhipster', + } as Alert) + ); + })); + + it('should produce a success message with custom position', inject([AlertService], (service: AlertService) => { + expect(service.addAlert({ type: 'success', message: 'Hello Jhipster', position: 'bottom left' })).toEqual( + expect.objectContaining({ + type: 'success', + message: 'Hello Jhipster', + position: 'bottom left', + } as Alert) + ); + })); + + it('should produce a error message', inject([AlertService], (service: AlertService) => { + expect(service.addAlert({ type: 'danger', message: 'Hello Jhipster' })).toEqual( + expect.objectContaining({ + type: 'danger', + message: 'Hello Jhipster', + } as Alert) + ); + })); + + it('should produce a warning message', inject([AlertService], (service: AlertService) => { + expect(service.addAlert({ type: 'warning', message: 'Hello Jhipster' })).toEqual( + expect.objectContaining({ + type: 'warning', + message: 'Hello Jhipster', + } as Alert) + ); + })); + + it('should produce a info message', inject([AlertService], (service: AlertService) => { + expect(service.addAlert({ type: 'info', message: 'Hello Jhipster' })).toEqual( + expect.objectContaining({ + type: 'info', + message: 'Hello Jhipster', + } as Alert) + ); + })); + + it('should produce a info message with translated message if key exists', inject( + [AlertService, TranslateService], + (service: AlertService, translateService: TranslateService) => { + translateService.setTranslation('en', { + 'hello.jhipster': 'Translated message', + }); + expect(service.addAlert({ type: 'info', message: 'Hello Jhipster', translationKey: 'hello.jhipster' })).toEqual( + expect.objectContaining({ + type: 'info', + message: 'Translated message', + } as Alert) + ); + } + )); + + it('should produce a info message with provided message if key does not exists', inject( + [AlertService, TranslateService], + (service: AlertService) => { + expect(service.addAlert({ type: 'info', message: 'Hello Jhipster', translationKey: 'hello.jhipster' })).toEqual( + expect.objectContaining({ + type: 'info', + message: 'Hello Jhipster', + } as Alert) + ); + } + )); + + it('should produce a info message with provided key if transltion key does not exist in translations and message is not provided', inject( + [AlertService, TranslateService], + (service: AlertService) => { + expect(service.addAlert({ type: 'info', translationKey: 'hello.jhipster' })).toEqual( + expect.objectContaining({ + type: 'info', + message: 'hello.jhipster', + } as Alert) + ); + } + )); + }); +}); diff --git a/src/main/webapp/app/core/util/alert.service.ts b/src/main/webapp/app/core/util/alert.service.ts new file mode 100644 index 00000000..4469bd6c --- /dev/null +++ b/src/main/webapp/app/core/util/alert.service.ts @@ -0,0 +1,95 @@ +import { Injectable, SecurityContext, NgZone } from '@angular/core'; +import { DomSanitizer } from '@angular/platform-browser'; +import { TranslateService } from '@ngx-translate/core'; + +import { translationNotFoundMessage } from 'app/config/translation.config'; + +export type AlertType = 'success' | 'danger' | 'warning' | 'info'; + +export interface Alert { + id?: number; + type: AlertType; + message?: string; + translationKey?: string; + translationParams?: { [key: string]: unknown }; + timeout?: number; + toast?: boolean; + position?: string; + close?: (alerts: Alert[]) => void; +} + +@Injectable({ + providedIn: 'root', +}) +export class AlertService { + timeout = 5000; + toast = false; + position = 'top right'; + + // unique id for each alert. Starts from 0. + private alertId = 0; + private alerts: Alert[] = []; + + constructor(private sanitizer: DomSanitizer, private ngZone: NgZone, private translateService: TranslateService) {} + + clear(): void { + this.alerts = []; + } + + get(): Alert[] { + return this.alerts; + } + + /** + * Adds alert to alerts array and returns added alert. + * @param alert Alert to add. If `timeout`, `toast` or `position` is missing then applying default value. + * If `translateKey` is available then it's translation else `message` is used for showing. + * @param extAlerts If missing then adding `alert` to `AlertService` internal array and alerts can be retrieved by `get()`. + * Else adding `alert` to `extAlerts`. + * @returns Added alert + */ + addAlert(alert: Alert, extAlerts?: Alert[]): Alert { + alert.id = this.alertId++; + + if (alert.translationKey) { + const translatedMessage = this.translateService.instant(alert.translationKey, alert.translationParams); + // if translation key exists + if (translatedMessage !== `${translationNotFoundMessage}[${alert.translationKey}]`) { + alert.message = translatedMessage; + } else if (!alert.message) { + alert.message = alert.translationKey; + } + } + + alert.message = this.sanitizer.sanitize(SecurityContext.HTML, alert.message ?? '') ?? ''; + alert.timeout = alert.timeout ?? this.timeout; + alert.toast = alert.toast ?? this.toast; + alert.position = alert.position ?? this.position; + alert.close = (alertsArray: Alert[]) => this.closeAlert(alert.id!, alertsArray); + + (extAlerts ?? this.alerts).push(alert); + + if (alert.timeout > 0) { + // Workaround protractor waiting for setTimeout. + // Reference https://www.protractortest.org/#/timeouts + this.ngZone.runOutsideAngular(() => { + setTimeout(() => { + this.ngZone.run(() => { + this.closeAlert(alert.id!, extAlerts ?? this.alerts); + }); + }, alert.timeout); + }); + } + + return alert; + } + + private closeAlert(alertId: number, extAlerts?: Alert[]): void { + const alerts = extAlerts ?? this.alerts; + const alertIndex = alerts.map(alert => alert.id).indexOf(alertId); + // if found alert then remove + if (alertIndex >= 0) { + alerts.splice(alertIndex, 1); + } + } +} diff --git a/src/main/webapp/app/core/util/data-util.service.spec.ts b/src/main/webapp/app/core/util/data-util.service.spec.ts new file mode 100644 index 00000000..fccbcc64 --- /dev/null +++ b/src/main/webapp/app/core/util/data-util.service.spec.ts @@ -0,0 +1,34 @@ +import { TestBed } from '@angular/core/testing'; + +import { DataUtils } from './data-util.service'; + +describe('Data Utils Service Test', () => { + let service: DataUtils; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [DataUtils], + }); + service = TestBed.inject(DataUtils); + }); + + describe('byteSize', () => { + it('should return the bytesize of the text', () => { + expect(service.byteSize('Hello JHipster')).toBe(`10.5 bytes`); + }); + }); + + describe('openFile', () => { + it('should open the file in the new window', () => { + const newWindow = { ...window }; + newWindow.document.write = jest.fn(); + window.open = jest.fn(() => newWindow); + window.URL.createObjectURL = jest.fn(); + // 'JHipster' in base64 is 'SkhpcHN0ZXI=' + const data = 'SkhpcHN0ZXI='; + const contentType = 'text/plain'; + service.openFile(data, contentType); + expect(window.open).toHaveBeenCalledTimes(1); + }); + }); +}); diff --git a/src/main/webapp/app/core/util/data-util.service.ts b/src/main/webapp/app/core/util/data-util.service.ts new file mode 100644 index 00000000..8b8d2bf9 --- /dev/null +++ b/src/main/webapp/app/core/util/data-util.service.ts @@ -0,0 +1,129 @@ +import { Injectable } from '@angular/core'; +import { FormGroup } from '@angular/forms'; +import { Observable, Observer } from 'rxjs'; + +export type FileLoadErrorType = 'not.image' | 'could.not.extract'; + +export interface FileLoadError { + message: string; + key: FileLoadErrorType; + params?: any; +} + +/** + * An utility service for data. + */ +@Injectable({ + providedIn: 'root', +}) +export class DataUtils { + /** + * Method to find the byte size of the string provides + */ + byteSize(base64String: string): string { + return this.formatAsBytes(this.size(base64String)); + } + + /** + * Method to open file + */ + openFile(data: string, contentType: string | null | undefined): void { + contentType = contentType ?? ''; + + const byteCharacters = atob(data); + const byteNumbers = new Array(byteCharacters.length); + for (let i = 0; i < byteCharacters.length; i++) { + byteNumbers[i] = byteCharacters.charCodeAt(i); + } + const byteArray = new Uint8Array(byteNumbers); + const blob = new Blob([byteArray], { + type: contentType, + }); + const fileURL = window.URL.createObjectURL(blob); + const win = window.open(fileURL); + win!.onload = function () { + URL.revokeObjectURL(fileURL); + }; + } + + /** + * Sets the base 64 data & file type of the 1st file on the event (event.target.files[0]) in the passed entity object + * and returns an observable. + * + * @param event the object containing the file (at event.target.files[0]) + * @param editForm the form group where the input field is located + * @param field the field name to set the file's 'base 64 data' on + * @param isImage boolean representing if the file represented by the event is an image + * @returns an observable that loads file to form field and completes if sussessful + * or returns error as FileLoadError on failure + */ + loadFileToForm(event: Event, editForm: FormGroup, field: string, isImage: boolean): Observable { + return new Observable((observer: Observer) => { + const eventTarget: HTMLInputElement | null = event.target as HTMLInputElement | null; + if (eventTarget?.files?.[0]) { + const file: File = eventTarget.files[0]; + if (isImage && !file.type.startsWith('image/')) { + const error: FileLoadError = { + message: `File was expected to be an image but was found to be '${file.type}'`, + key: 'not.image', + params: { fileType: file.type }, + }; + observer.error(error); + } else { + const fieldContentType: string = field + 'ContentType'; + this.toBase64(file, (base64Data: string) => { + editForm.patchValue({ + [field]: base64Data, + [fieldContentType]: file.type, + }); + observer.next(); + observer.complete(); + }); + } + } else { + const error: FileLoadError = { + message: 'Could not extract file', + key: 'could.not.extract', + params: { event }, + }; + observer.error(error); + } + }); + } + + /** + * Method to convert the file to base64 + */ + private toBase64(file: File, callback: (base64Data: string) => void): void { + const fileReader: FileReader = new FileReader(); + fileReader.onload = (e: ProgressEvent) => { + if (typeof e.target?.result === 'string') { + const base64Data: string = e.target.result.substring(e.target.result.indexOf('base64,') + 'base64,'.length); + callback(base64Data); + } + }; + fileReader.readAsDataURL(file); + } + + private endsWith(suffix: string, str: string): boolean { + return str.includes(suffix, str.length - suffix.length); + } + + private paddingSize(value: string): number { + if (this.endsWith('==', value)) { + return 2; + } + if (this.endsWith('=', value)) { + return 1; + } + return 0; + } + + private size(value: string): number { + return (value.length / 4) * 3 - this.paddingSize(value); + } + + private formatAsBytes(size: number): string { + return size.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ' ') + ' bytes'; // NOSONAR + } +} diff --git a/src/main/webapp/app/core/util/event-manager.service.spec.ts b/src/main/webapp/app/core/util/event-manager.service.spec.ts new file mode 100644 index 00000000..910009c9 --- /dev/null +++ b/src/main/webapp/app/core/util/event-manager.service.spec.ts @@ -0,0 +1,84 @@ +import { inject, TestBed } from '@angular/core/testing'; + +import { EventManager, EventWithContent } from './event-manager.service'; + +describe('Event Manager tests', () => { + describe('EventWithContent', () => { + it('should create correctly EventWithContent', () => { + // WHEN + const eventWithContent = new EventWithContent('name', 'content'); + + // THEN + expect(eventWithContent).toEqual({ name: 'name', content: 'content' }); + }); + }); + + describe('EventManager', () => { + let recievedEvent: EventWithContent | string | null; + + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [EventManager], + }); + recievedEvent = null; + }); + + it('should not fail when nosubscriber and broadcasting', inject([EventManager], (eventManager: EventManager) => { + expect(eventManager.observer).toBeUndefined(); + eventManager.broadcast({ name: 'modifier', content: 'modified something' }); + })); + + it('should create an observable and callback when broadcasted EventWithContent', inject( + [EventManager], + (eventManager: EventManager) => { + // GIVEN + eventManager.subscribe('modifier', (event: EventWithContent | string) => (recievedEvent = event)); + + // WHEN + eventManager.broadcast({ name: 'unrelatedModifier', content: 'unreleated modification' }); + // THEN + expect(recievedEvent).toBeNull(); + + // WHEN + eventManager.broadcast({ name: 'modifier', content: 'modified something' }); + // THEN + expect(recievedEvent).toEqual({ name: 'modifier', content: 'modified something' }); + } + )); + + it('should create an observable and callback when broadcasted string', inject([EventManager], (eventManager: EventManager) => { + // GIVEN + eventManager.subscribe('modifier', (event: EventWithContent | string) => (recievedEvent = event)); + + // WHEN + eventManager.broadcast('unrelatedModifier'); + // THEN + expect(recievedEvent).toBeNull(); + + // WHEN + eventManager.broadcast('modifier'); + // THEN + expect(recievedEvent).toEqual('modifier'); + })); + + it('should subscribe to multiple events', inject([EventManager], (eventManager: EventManager) => { + // GIVEN + eventManager.subscribe(['modifier', 'modifier2'], (event: EventWithContent | string) => (recievedEvent = event)); + + // WHEN + eventManager.broadcast('unrelatedModifier'); + // THEN + expect(recievedEvent).toBeNull(); + + // WHEN + eventManager.broadcast({ name: 'modifier', content: 'modified something' }); + // THEN + expect(recievedEvent).toEqual({ name: 'modifier', content: 'modified something' }); + + // WHEN + eventManager.broadcast('modifier2'); + // THEN + expect(recievedEvent).toEqual('modifier2'); + })); + }); +}); diff --git a/src/main/webapp/app/core/util/event-manager.service.ts b/src/main/webapp/app/core/util/event-manager.service.ts new file mode 100644 index 00000000..1730369b --- /dev/null +++ b/src/main/webapp/app/core/util/event-manager.service.ts @@ -0,0 +1,63 @@ +import { Injectable } from '@angular/core'; +import { Observable, Observer, Subscription } from 'rxjs'; +import { filter, share } from 'rxjs/operators'; + +export class EventWithContent { + constructor(public name: string, public content: T) {} +} + +/** + * An utility class to manage RX events + */ +@Injectable({ + providedIn: 'root', +}) +export class EventManager { + observable: Observable | string>; + observer?: Observer | string>; + + constructor() { + this.observable = new Observable((observer: Observer | string>) => { + this.observer = observer; + }).pipe(share()); + } + + /** + * Method to broadcast the event to observer + */ + broadcast(event: EventWithContent | string): void { + if (this.observer) { + this.observer.next(event); + } + } + + /** + * Method to subscribe to an event with callback + * @param eventNames Single event name or array of event names to what subscribe + * @param callback Callback to run when the event occurs + */ + subscribe(eventNames: string | string[], callback: (event: EventWithContent | string) => void): Subscription { + if (typeof eventNames === 'string') { + eventNames = [eventNames]; + } + return this.observable + .pipe( + filter((event: EventWithContent | string) => { + for (const eventName of eventNames) { + if ((typeof event === 'string' && event === eventName) || (typeof event !== 'string' && event.name === eventName)) { + return true; + } + } + return false; + }) + ) + .subscribe(callback); + } + + /** + * Method to unsubscribe the subscription + */ + destroy(subscriber: Subscription): void { + subscriber.unsubscribe(); + } +} diff --git a/src/main/webapp/app/core/util/operators.spec.ts b/src/main/webapp/app/core/util/operators.spec.ts new file mode 100644 index 00000000..429647c4 --- /dev/null +++ b/src/main/webapp/app/core/util/operators.spec.ts @@ -0,0 +1,18 @@ +import { filterNaN, isPresent } from './operators'; + +describe('Operators Test', () => { + describe('isPresent', () => { + it('should remove null and undefined values', () => { + expect([1, null, undefined].filter(isPresent)).toEqual([1]); + }); + }); + + describe('filterNaN', () => { + it('should return 0 for NaN', () => { + expect(filterNaN(NaN)).toBe(0); + }); + it('should return number for a number', () => { + expect(filterNaN(12345)).toBe(12345); + }); + }); +}); diff --git a/src/main/webapp/app/core/util/operators.ts b/src/main/webapp/app/core/util/operators.ts new file mode 100644 index 00000000..c2245929 --- /dev/null +++ b/src/main/webapp/app/core/util/operators.ts @@ -0,0 +1,9 @@ +/* + * Function used to workaround https://github.com/microsoft/TypeScript/issues/16069 + * es2019 alternative `const filteredArr = myArr.flatMap((x) => x ? x : []);` + */ +export function isPresent(t: T | undefined | null | void): t is T { + return t !== undefined && t !== null; +} + +export const filterNaN = (input: number): number => (isNaN(input) ? 0 : input); diff --git a/src/main/webapp/app/core/util/parse-links.service.spec.ts b/src/main/webapp/app/core/util/parse-links.service.spec.ts new file mode 100644 index 00000000..40b6c75c --- /dev/null +++ b/src/main/webapp/app/core/util/parse-links.service.spec.ts @@ -0,0 +1,36 @@ +import { inject, TestBed } from '@angular/core/testing'; + +import { ParseLinks } from './parse-links.service'; + +describe('Parse links service test', () => { + describe('Parse Links Service Test', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [ParseLinks], + }); + }); + + it('should throw an error when passed an empty string', inject([ParseLinks], (service: ParseLinks) => { + expect(function () { + service.parse(''); + }).toThrow(new Error('input must not be of zero length')); + })); + + it('should throw an error when passed without comma', inject([ParseLinks], (service: ParseLinks) => { + expect(function () { + service.parse('test'); + }).toThrow(new Error('section could not be split on ";"')); + })); + + it('should throw an error when passed without semicolon', inject([ParseLinks], (service: ParseLinks) => { + expect(function () { + service.parse('test,test2'); + }).toThrow(new Error('section could not be split on ";"')); + })); + + it('should return links when headers are passed', inject([ParseLinks], (service: ParseLinks) => { + const links = { last: 0, first: 0 }; + expect(service.parse(' ; rel="last",; rel="first"')).toEqual(links); + })); + }); +}); diff --git a/src/main/webapp/app/core/util/parse-links.service.ts b/src/main/webapp/app/core/util/parse-links.service.ts new file mode 100644 index 00000000..dc1eb0e9 --- /dev/null +++ b/src/main/webapp/app/core/util/parse-links.service.ts @@ -0,0 +1,47 @@ +import { Injectable } from '@angular/core'; + +/** + * An utility service for link parsing. + */ +@Injectable({ + providedIn: 'root', +}) +export class ParseLinks { + /** + * Method to parse the links + */ + parse(header: string): { [key: string]: number } { + if (header.length === 0) { + throw new Error('input must not be of zero length'); + } + + // Split parts by comma + const parts: string[] = header.split(','); + const links: { [key: string]: number } = {}; + + // Parse each part into a named link + parts.forEach(p => { + const section: string[] = p.split(';'); + + if (section.length !== 2) { + throw new Error('section could not be split on ";"'); + } + + const url: string = section[0].replace(/<(.*)>/, '$1').trim(); // NOSONAR + const queryString: { [key: string]: string | undefined } = {}; + + url.replace(/([^?=&]+)(=([^&]*))?/g, (_$0: string, $1: string | undefined, _$2: string | undefined, $3: string | undefined) => { + if ($1 !== undefined) { + queryString[$1] = $3; + } + return $3 ?? ''; + }); + + if (queryString.page !== undefined) { + const name: string = section[1].replace(/rel="(.*)"/, '$1').trim(); + links[name] = parseInt(queryString.page, 10); + } + }); + return links; + } +} diff --git a/src/main/webapp/app/entities/blood-pressure/blood-pressure.model.ts b/src/main/webapp/app/entities/blood-pressure/blood-pressure.model.ts new file mode 100644 index 00000000..d2d7c6b4 --- /dev/null +++ b/src/main/webapp/app/entities/blood-pressure/blood-pressure.model.ts @@ -0,0 +1,12 @@ +import dayjs from 'dayjs/esm'; +import { IUser } from 'app/entities/user/user.model'; + +export interface IBloodPressure { + id: number; + timestamp?: dayjs.Dayjs | null; + systolic?: number | null; + diastolic?: number | null; + user?: Pick | null; +} + +export type NewBloodPressure = Omit & { id: null }; diff --git a/src/main/webapp/app/entities/blood-pressure/blood-pressure.module.ts b/src/main/webapp/app/entities/blood-pressure/blood-pressure.module.ts new file mode 100644 index 00000000..3439a486 --- /dev/null +++ b/src/main/webapp/app/entities/blood-pressure/blood-pressure.module.ts @@ -0,0 +1,13 @@ +import { NgModule } from '@angular/core'; +import { SharedModule } from 'app/shared/shared.module'; +import { BloodPressureComponent } from './list/blood-pressure.component'; +import { BloodPressureDetailComponent } from './detail/blood-pressure-detail.component'; +import { BloodPressureUpdateComponent } from './update/blood-pressure-update.component'; +import { BloodPressureDeleteDialogComponent } from './delete/blood-pressure-delete-dialog.component'; +import { BloodPressureRoutingModule } from './route/blood-pressure-routing.module'; + +@NgModule({ + imports: [SharedModule, BloodPressureRoutingModule], + declarations: [BloodPressureComponent, BloodPressureDetailComponent, BloodPressureUpdateComponent, BloodPressureDeleteDialogComponent], +}) +export class BloodPressureModule {} diff --git a/src/main/webapp/app/entities/blood-pressure/blood-pressure.test-samples.ts b/src/main/webapp/app/entities/blood-pressure/blood-pressure.test-samples.ts new file mode 100644 index 00000000..12dc4791 --- /dev/null +++ b/src/main/webapp/app/entities/blood-pressure/blood-pressure.test-samples.ts @@ -0,0 +1,36 @@ +import dayjs from 'dayjs/esm'; + +import { IBloodPressure, NewBloodPressure } from './blood-pressure.model'; + +export const sampleWithRequiredData: IBloodPressure = { + id: 82246, + timestamp: dayjs('2022-11-07T19:21'), + systolic: 50366, + diastolic: 28757, +}; + +export const sampleWithPartialData: IBloodPressure = { + id: 37469, + timestamp: dayjs('2022-11-07T18:12'), + systolic: 94935, + diastolic: 2497, +}; + +export const sampleWithFullData: IBloodPressure = { + id: 74963, + timestamp: dayjs('2022-11-07T19:48'), + systolic: 81126, + diastolic: 83758, +}; + +export const sampleWithNewData: NewBloodPressure = { + timestamp: dayjs('2022-11-07T01:01'), + systolic: 64105, + diastolic: 1733, + id: null, +}; + +Object.freeze(sampleWithNewData); +Object.freeze(sampleWithRequiredData); +Object.freeze(sampleWithPartialData); +Object.freeze(sampleWithFullData); diff --git a/src/main/webapp/app/entities/blood-pressure/delete/blood-pressure-delete-dialog.component.html b/src/main/webapp/app/entities/blood-pressure/delete/blood-pressure-delete-dialog.component.html new file mode 100644 index 00000000..cdc58249 --- /dev/null +++ b/src/main/webapp/app/entities/blood-pressure/delete/blood-pressure-delete-dialog.component.html @@ -0,0 +1,28 @@ +
+ + + + + +
diff --git a/src/main/webapp/app/entities/blood-pressure/delete/blood-pressure-delete-dialog.component.spec.ts b/src/main/webapp/app/entities/blood-pressure/delete/blood-pressure-delete-dialog.component.spec.ts new file mode 100644 index 00000000..21dbcd41 --- /dev/null +++ b/src/main/webapp/app/entities/blood-pressure/delete/blood-pressure-delete-dialog.component.spec.ts @@ -0,0 +1,63 @@ +jest.mock('@ng-bootstrap/ng-bootstrap'); + +import { ComponentFixture, TestBed, inject, fakeAsync, tick } from '@angular/core/testing'; +import { HttpResponse } from '@angular/common/http'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { of } from 'rxjs'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; + +import { BloodPressureService } from '../service/blood-pressure.service'; + +import { BloodPressureDeleteDialogComponent } from './blood-pressure-delete-dialog.component'; + +describe('BloodPressure Management Delete Component', () => { + let comp: BloodPressureDeleteDialogComponent; + let fixture: ComponentFixture; + let service: BloodPressureService; + let mockActiveModal: NgbActiveModal; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + declarations: [BloodPressureDeleteDialogComponent], + providers: [NgbActiveModal], + }) + .overrideTemplate(BloodPressureDeleteDialogComponent, '') + .compileComponents(); + fixture = TestBed.createComponent(BloodPressureDeleteDialogComponent); + comp = fixture.componentInstance; + service = TestBed.inject(BloodPressureService); + mockActiveModal = TestBed.inject(NgbActiveModal); + }); + + describe('confirmDelete', () => { + it('Should call delete service on confirmDelete', inject( + [], + fakeAsync(() => { + // GIVEN + jest.spyOn(service, 'delete').mockReturnValue(of(new HttpResponse({ body: {} }))); + + // WHEN + comp.confirmDelete(123); + tick(); + + // THEN + expect(service.delete).toHaveBeenCalledWith(123); + expect(mockActiveModal.close).toHaveBeenCalledWith('deleted'); + }) + )); + + it('Should not call delete service on clear', () => { + // GIVEN + jest.spyOn(service, 'delete'); + + // WHEN + comp.cancel(); + + // THEN + expect(service.delete).not.toHaveBeenCalled(); + expect(mockActiveModal.close).not.toHaveBeenCalled(); + expect(mockActiveModal.dismiss).toHaveBeenCalled(); + }); + }); +}); diff --git a/src/main/webapp/app/entities/blood-pressure/delete/blood-pressure-delete-dialog.component.ts b/src/main/webapp/app/entities/blood-pressure/delete/blood-pressure-delete-dialog.component.ts new file mode 100644 index 00000000..6e87efc7 --- /dev/null +++ b/src/main/webapp/app/entities/blood-pressure/delete/blood-pressure-delete-dialog.component.ts @@ -0,0 +1,25 @@ +import { Component } from '@angular/core'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; + +import { IBloodPressure } from '../blood-pressure.model'; +import { BloodPressureService } from '../service/blood-pressure.service'; +import { ITEM_DELETED_EVENT } from 'app/config/navigation.constants'; + +@Component({ + templateUrl: './blood-pressure-delete-dialog.component.html', +}) +export class BloodPressureDeleteDialogComponent { + bloodPressure?: IBloodPressure; + + constructor(protected bloodPressureService: BloodPressureService, protected activeModal: NgbActiveModal) {} + + cancel(): void { + this.activeModal.dismiss(); + } + + confirmDelete(id: number): void { + this.bloodPressureService.delete(id).subscribe(() => { + this.activeModal.close(ITEM_DELETED_EVENT); + }); + } +} diff --git a/src/main/webapp/app/entities/blood-pressure/detail/blood-pressure-detail.component.html b/src/main/webapp/app/entities/blood-pressure/detail/blood-pressure-detail.component.html new file mode 100644 index 00000000..253d4df1 --- /dev/null +++ b/src/main/webapp/app/entities/blood-pressure/detail/blood-pressure-detail.component.html @@ -0,0 +1,46 @@ +
+
+
+

+ Blood Pressure +

+ +
+ + + + + +
+
ID
+
+ {{ bloodPressure.id }} +
+
Timestamp
+
+ {{ bloodPressure.timestamp | formatMediumDatetime }} +
+
Systolic
+
+ {{ bloodPressure.systolic }} +
+
Diastolic
+
+ {{ bloodPressure.diastolic }} +
+
User
+
+ {{ bloodPressure.user?.login }} +
+
+ + + + +
+
+
diff --git a/src/main/webapp/app/entities/blood-pressure/detail/blood-pressure-detail.component.spec.ts b/src/main/webapp/app/entities/blood-pressure/detail/blood-pressure-detail.component.spec.ts new file mode 100644 index 00000000..f7400d84 --- /dev/null +++ b/src/main/webapp/app/entities/blood-pressure/detail/blood-pressure-detail.component.spec.ts @@ -0,0 +1,36 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ActivatedRoute } from '@angular/router'; +import { of } from 'rxjs'; + +import { BloodPressureDetailComponent } from './blood-pressure-detail.component'; + +describe('BloodPressure Management Detail Component', () => { + let comp: BloodPressureDetailComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [BloodPressureDetailComponent], + providers: [ + { + provide: ActivatedRoute, + useValue: { data: of({ bloodPressure: { id: 123 } }) }, + }, + ], + }) + .overrideTemplate(BloodPressureDetailComponent, '') + .compileComponents(); + fixture = TestBed.createComponent(BloodPressureDetailComponent); + comp = fixture.componentInstance; + }); + + describe('OnInit', () => { + it('Should load bloodPressure on init', () => { + // WHEN + comp.ngOnInit(); + + // THEN + expect(comp.bloodPressure).toEqual(expect.objectContaining({ id: 123 })); + }); + }); +}); diff --git a/src/main/webapp/app/entities/blood-pressure/detail/blood-pressure-detail.component.ts b/src/main/webapp/app/entities/blood-pressure/detail/blood-pressure-detail.component.ts new file mode 100644 index 00000000..422d55f2 --- /dev/null +++ b/src/main/webapp/app/entities/blood-pressure/detail/blood-pressure-detail.component.ts @@ -0,0 +1,24 @@ +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; + +import { IBloodPressure } from '../blood-pressure.model'; + +@Component({ + selector: 'jhi-blood-pressure-detail', + templateUrl: './blood-pressure-detail.component.html', +}) +export class BloodPressureDetailComponent implements OnInit { + bloodPressure: IBloodPressure | null = null; + + constructor(protected activatedRoute: ActivatedRoute) {} + + ngOnInit(): void { + this.activatedRoute.data.subscribe(({ bloodPressure }) => { + this.bloodPressure = bloodPressure; + }); + } + + previousState(): void { + window.history.back(); + } +} diff --git a/src/main/webapp/app/entities/blood-pressure/list/blood-pressure.component.html b/src/main/webapp/app/entities/blood-pressure/list/blood-pressure.component.html new file mode 100644 index 00000000..3b1a7af6 --- /dev/null +++ b/src/main/webapp/app/entities/blood-pressure/list/blood-pressure.component.html @@ -0,0 +1,142 @@ +
+

+ Blood Pressures + +
+ + + +
+

+ + + + + +
+
+
+ + + + + + +
+
+
+ +
+ No Blood Pressures found +
+ +
+ + + + + + + + + + + + + + + + + + + + + +
+
+ ID + +
+
+
+ Timestamp + +
+
+
+ Systolic + +
+
+
+ Diastolic + +
+
+
+ User + +
+
+ {{ bloodPressure.id }} + {{ bloodPressure.timestamp | formatMediumDatetime }}{{ bloodPressure.systolic }}{{ bloodPressure.diastolic }} + {{ bloodPressure.user?.login }} + +
+ + + + + +
+
+
+
diff --git a/src/main/webapp/app/entities/blood-pressure/list/blood-pressure.component.spec.ts b/src/main/webapp/app/entities/blood-pressure/list/blood-pressure.component.spec.ts new file mode 100644 index 00000000..7339b030 --- /dev/null +++ b/src/main/webapp/app/entities/blood-pressure/list/blood-pressure.component.spec.ts @@ -0,0 +1,124 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { HttpHeaders, HttpResponse } from '@angular/common/http'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { ActivatedRoute } from '@angular/router'; +import { RouterTestingModule } from '@angular/router/testing'; +import { of } from 'rxjs'; + +import { BloodPressureService } from '../service/blood-pressure.service'; + +import { BloodPressureComponent } from './blood-pressure.component'; +import SpyInstance = jest.SpyInstance; + +describe('BloodPressure Management Component', () => { + let comp: BloodPressureComponent; + let fixture: ComponentFixture; + let service: BloodPressureService; + let routerNavigateSpy: SpyInstance>; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [RouterTestingModule.withRoutes([{ path: 'blood-pressure', component: BloodPressureComponent }]), HttpClientTestingModule], + declarations: [BloodPressureComponent], + providers: [ + { + provide: ActivatedRoute, + useValue: { + data: of({ + defaultSort: 'id,asc', + }), + queryParamMap: of( + jest.requireActual('@angular/router').convertToParamMap({ + page: '1', + size: '1', + sort: 'id,desc', + }) + ), + snapshot: { queryParams: {} }, + }, + }, + ], + }) + .overrideTemplate(BloodPressureComponent, '') + .compileComponents(); + + fixture = TestBed.createComponent(BloodPressureComponent); + comp = fixture.componentInstance; + service = TestBed.inject(BloodPressureService); + routerNavigateSpy = jest.spyOn(comp.router, 'navigate'); + + const headers = new HttpHeaders(); + jest.spyOn(service, 'query').mockReturnValue( + of( + new HttpResponse({ + body: [{ id: 123 }], + headers, + }) + ) + ); + }); + + it('Should call load all on init', () => { + // WHEN + comp.ngOnInit(); + + // THEN + expect(service.query).toHaveBeenCalled(); + expect(comp.bloodPressures?.[0]).toEqual(expect.objectContaining({ id: 123 })); + }); + + describe('trackId', () => { + it('Should forward to bloodPressureService', () => { + const entity = { id: 123 }; + jest.spyOn(service, 'getBloodPressureIdentifier'); + const id = comp.trackId(0, entity); + expect(service.getBloodPressureIdentifier).toHaveBeenCalledWith(entity); + expect(id).toBe(entity.id); + }); + }); + + it('should load a page', () => { + // WHEN + comp.navigateToPage(1); + + // THEN + expect(routerNavigateSpy).toHaveBeenCalled(); + }); + + it('should calculate the sort attribute for an id', () => { + // WHEN + comp.ngOnInit(); + + // THEN + expect(service.query).toHaveBeenLastCalledWith(expect.objectContaining({ sort: ['id,desc'] })); + }); + + it('should calculate the sort attribute for a non-id attribute', () => { + // GIVEN + comp.predicate = 'name'; + + // WHEN + comp.navigateToWithComponentValues(); + + // THEN + expect(routerNavigateSpy).toHaveBeenLastCalledWith( + expect.anything(), + expect.objectContaining({ + queryParams: expect.objectContaining({ + sort: ['name,asc'], + }), + }) + ); + }); + + it('should re-initialize the page', () => { + // WHEN + comp.loadPage(1); + comp.reset(); + + // THEN + expect(comp.page).toEqual(1); + expect(service.query).toHaveBeenCalledTimes(2); + expect(comp.bloodPressures?.[0]).toEqual(expect.objectContaining({ id: 123 })); + }); +}); diff --git a/src/main/webapp/app/entities/blood-pressure/list/blood-pressure.component.ts b/src/main/webapp/app/entities/blood-pressure/list/blood-pressure.component.ts new file mode 100644 index 00000000..130eb18e --- /dev/null +++ b/src/main/webapp/app/entities/blood-pressure/list/blood-pressure.component.ts @@ -0,0 +1,185 @@ +import { Component, OnInit } from '@angular/core'; +import { HttpHeaders } from '@angular/common/http'; +import { ActivatedRoute, Data, ParamMap, Router } from '@angular/router'; +import { combineLatest, filter, Observable, switchMap, tap } from 'rxjs'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; + +import { IBloodPressure } from '../blood-pressure.model'; + +import { ITEMS_PER_PAGE } from 'app/config/pagination.constants'; +import { ASC, DESC, SORT, ITEM_DELETED_EVENT, DEFAULT_SORT_DATA } from 'app/config/navigation.constants'; +import { EntityArrayResponseType, BloodPressureService } from '../service/blood-pressure.service'; +import { BloodPressureDeleteDialogComponent } from '../delete/blood-pressure-delete-dialog.component'; +import { ParseLinks } from 'app/core/util/parse-links.service'; + +@Component({ + selector: 'jhi-blood-pressure', + templateUrl: './blood-pressure.component.html', +}) +export class BloodPressureComponent implements OnInit { + bloodPressures?: IBloodPressure[]; + isLoading = false; + + predicate = 'id'; + ascending = true; + currentSearch = ''; + + itemsPerPage = ITEMS_PER_PAGE; + links: { [key: string]: number } = { + last: 0, + }; + page = 1; + + constructor( + protected bloodPressureService: BloodPressureService, + protected activatedRoute: ActivatedRoute, + public router: Router, + protected parseLinks: ParseLinks, + protected modalService: NgbModal + ) {} + + reset(): void { + this.page = 1; + this.bloodPressures = []; + this.load(); + } + + loadPage(page: number): void { + this.page = page; + this.load(); + } + + trackId = (_index: number, item: IBloodPressure): number => this.bloodPressureService.getBloodPressureIdentifier(item); + + search(query: string): void { + this.page = 1; + this.currentSearch = query; + this.navigateToWithComponentValues(); + } + + ngOnInit(): void { + this.load(); + } + + delete(bloodPressure: IBloodPressure): void { + const modalRef = this.modalService.open(BloodPressureDeleteDialogComponent, { size: 'lg', backdrop: 'static' }); + modalRef.componentInstance.bloodPressure = bloodPressure; + // unsubscribe not needed because closed completes on modal close + modalRef.closed + .pipe( + filter(reason => reason === ITEM_DELETED_EVENT), + switchMap(() => this.loadFromBackendWithRouteInformations()) + ) + .subscribe({ + next: (res: EntityArrayResponseType) => { + this.onResponseSuccess(res); + }, + }); + } + + load(): void { + this.loadFromBackendWithRouteInformations().subscribe({ + next: (res: EntityArrayResponseType) => { + this.onResponseSuccess(res); + }, + }); + } + + navigateToWithComponentValues(): void { + this.handleNavigation(this.page, this.predicate, this.ascending, this.currentSearch); + } + + navigateToPage(page = this.page): void { + this.handleNavigation(page, this.predicate, this.ascending, this.currentSearch); + } + + protected loadFromBackendWithRouteInformations(): Observable { + return combineLatest([this.activatedRoute.queryParamMap, this.activatedRoute.data]).pipe( + tap(([params, data]) => this.fillComponentAttributeFromRoute(params, data)), + switchMap(() => this.queryBackend(this.page, this.predicate, this.ascending, this.currentSearch)) + ); + } + + protected fillComponentAttributeFromRoute(params: ParamMap, data: Data): void { + const sort = (params.get(SORT) ?? data[DEFAULT_SORT_DATA]).split(','); + this.predicate = sort[0]; + this.ascending = sort[1] === ASC; + if (params.has('search') && params.get('search') !== '') { + this.currentSearch = params.get('search') as string; + } + } + + protected onResponseSuccess(response: EntityArrayResponseType): void { + this.fillComponentAttributesFromResponseHeader(response.headers); + const dataFromBody = this.fillComponentAttributesFromResponseBody(response.body); + this.bloodPressures = dataFromBody; + } + + protected fillComponentAttributesFromResponseBody(data: IBloodPressure[] | null): IBloodPressure[] { + const bloodPressuresNew = this.bloodPressures ?? []; + if (data) { + for (const d of data) { + if (bloodPressuresNew.map(op => op.id).indexOf(d.id) === -1) { + bloodPressuresNew.push(d); + } + } + } + return bloodPressuresNew; + } + + protected fillComponentAttributesFromResponseHeader(headers: HttpHeaders): void { + const linkHeader = headers.get('link'); + if (linkHeader) { + this.links = this.parseLinks.parse(linkHeader); + } else { + this.links = { + last: 0, + }; + } + } + + protected queryBackend( + page?: number, + predicate?: string, + ascending?: boolean, + currentSearch?: string + ): Observable { + this.isLoading = true; + const pageToLoad: number = page ?? 1; + const queryObject: any = { + page: pageToLoad - 1, + size: this.itemsPerPage, + eagerload: true, + query: currentSearch, + sort: this.getSortQueryParam(predicate, ascending), + }; + if (this.currentSearch && this.currentSearch !== '') { + return this.bloodPressureService.search(queryObject).pipe(tap(() => (this.isLoading = false))); + } else { + return this.bloodPressureService.query(queryObject).pipe(tap(() => (this.isLoading = false))); + } + } + + protected handleNavigation(page = this.page, predicate?: string, ascending?: boolean, currentSearch?: string): void { + const queryParamsObj = { + search: currentSearch, + page, + size: this.itemsPerPage, + sort: this.getSortQueryParam(predicate, ascending), + }; + + this.router.navigate(['./'], { + relativeTo: this.activatedRoute, + queryParams: queryParamsObj, + }); + } + + protected getSortQueryParam(predicate = this.predicate, ascending = this.ascending): string[] { + const ascendingQueryParam = ascending ? ASC : DESC; + if (predicate === '') { + return []; + } else { + return [predicate + ',' + ascendingQueryParam]; + } + } +} diff --git a/src/main/webapp/app/entities/blood-pressure/route/blood-pressure-routing-resolve.service.spec.ts b/src/main/webapp/app/entities/blood-pressure/route/blood-pressure-routing-resolve.service.spec.ts new file mode 100644 index 00000000..606b908c --- /dev/null +++ b/src/main/webapp/app/entities/blood-pressure/route/blood-pressure-routing-resolve.service.spec.ts @@ -0,0 +1,89 @@ +import { TestBed } from '@angular/core/testing'; +import { HttpResponse } from '@angular/common/http'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { ActivatedRouteSnapshot, ActivatedRoute, Router, convertToParamMap } from '@angular/router'; +import { RouterTestingModule } from '@angular/router/testing'; +import { of } from 'rxjs'; + +import { IBloodPressure } from '../blood-pressure.model'; +import { BloodPressureService } from '../service/blood-pressure.service'; + +import { BloodPressureRoutingResolveService } from './blood-pressure-routing-resolve.service'; + +describe('BloodPressure routing resolve service', () => { + let mockRouter: Router; + let mockActivatedRouteSnapshot: ActivatedRouteSnapshot; + let routingResolveService: BloodPressureRoutingResolveService; + let service: BloodPressureService; + let resultBloodPressure: IBloodPressure | null | undefined; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule, RouterTestingModule.withRoutes([])], + providers: [ + { + provide: ActivatedRoute, + useValue: { + snapshot: { + paramMap: convertToParamMap({}), + }, + }, + }, + ], + }); + mockRouter = TestBed.inject(Router); + jest.spyOn(mockRouter, 'navigate').mockImplementation(() => Promise.resolve(true)); + mockActivatedRouteSnapshot = TestBed.inject(ActivatedRoute).snapshot; + routingResolveService = TestBed.inject(BloodPressureRoutingResolveService); + service = TestBed.inject(BloodPressureService); + resultBloodPressure = undefined; + }); + + describe('resolve', () => { + it('should return IBloodPressure returned by find', () => { + // GIVEN + service.find = jest.fn(id => of(new HttpResponse({ body: { id } }))); + mockActivatedRouteSnapshot.params = { id: 123 }; + + // WHEN + routingResolveService.resolve(mockActivatedRouteSnapshot).subscribe(result => { + resultBloodPressure = result; + }); + + // THEN + expect(service.find).toBeCalledWith(123); + expect(resultBloodPressure).toEqual({ id: 123 }); + }); + + it('should return null if id is not provided', () => { + // GIVEN + service.find = jest.fn(); + mockActivatedRouteSnapshot.params = {}; + + // WHEN + routingResolveService.resolve(mockActivatedRouteSnapshot).subscribe(result => { + resultBloodPressure = result; + }); + + // THEN + expect(service.find).not.toBeCalled(); + expect(resultBloodPressure).toEqual(null); + }); + + it('should route to 404 page if data not found in server', () => { + // GIVEN + jest.spyOn(service, 'find').mockReturnValue(of(new HttpResponse({ body: null }))); + mockActivatedRouteSnapshot.params = { id: 123 }; + + // WHEN + routingResolveService.resolve(mockActivatedRouteSnapshot).subscribe(result => { + resultBloodPressure = result; + }); + + // THEN + expect(service.find).toBeCalledWith(123); + expect(resultBloodPressure).toEqual(undefined); + expect(mockRouter.navigate).toHaveBeenCalledWith(['404']); + }); + }); +}); diff --git a/src/main/webapp/app/entities/blood-pressure/route/blood-pressure-routing-resolve.service.ts b/src/main/webapp/app/entities/blood-pressure/route/blood-pressure-routing-resolve.service.ts new file mode 100644 index 00000000..0bde976a --- /dev/null +++ b/src/main/webapp/app/entities/blood-pressure/route/blood-pressure-routing-resolve.service.ts @@ -0,0 +1,30 @@ +import { Injectable } from '@angular/core'; +import { HttpResponse } from '@angular/common/http'; +import { Resolve, ActivatedRouteSnapshot, Router } from '@angular/router'; +import { Observable, of, EMPTY } from 'rxjs'; +import { mergeMap } from 'rxjs/operators'; + +import { IBloodPressure } from '../blood-pressure.model'; +import { BloodPressureService } from '../service/blood-pressure.service'; + +@Injectable({ providedIn: 'root' }) +export class BloodPressureRoutingResolveService implements Resolve { + constructor(protected service: BloodPressureService, protected router: Router) {} + + resolve(route: ActivatedRouteSnapshot): Observable { + const id = route.params['id']; + if (id) { + return this.service.find(id).pipe( + mergeMap((bloodPressure: HttpResponse) => { + if (bloodPressure.body) { + return of(bloodPressure.body); + } else { + this.router.navigate(['404']); + return EMPTY; + } + }) + ); + } + return of(null); + } +} diff --git a/src/main/webapp/app/entities/blood-pressure/route/blood-pressure-routing.module.ts b/src/main/webapp/app/entities/blood-pressure/route/blood-pressure-routing.module.ts new file mode 100644 index 00000000..d20b94bb --- /dev/null +++ b/src/main/webapp/app/entities/blood-pressure/route/blood-pressure-routing.module.ts @@ -0,0 +1,50 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; + +import { UserRouteAccessService } from 'app/core/auth/user-route-access.service'; +import { BloodPressureComponent } from '../list/blood-pressure.component'; +import { BloodPressureDetailComponent } from '../detail/blood-pressure-detail.component'; +import { BloodPressureUpdateComponent } from '../update/blood-pressure-update.component'; +import { BloodPressureRoutingResolveService } from './blood-pressure-routing-resolve.service'; +import { ASC } from 'app/config/navigation.constants'; + +const bloodPressureRoute: Routes = [ + { + path: '', + component: BloodPressureComponent, + data: { + defaultSort: 'id,' + ASC, + }, + canActivate: [UserRouteAccessService], + }, + { + path: ':id/view', + component: BloodPressureDetailComponent, + resolve: { + bloodPressure: BloodPressureRoutingResolveService, + }, + canActivate: [UserRouteAccessService], + }, + { + path: 'new', + component: BloodPressureUpdateComponent, + resolve: { + bloodPressure: BloodPressureRoutingResolveService, + }, + canActivate: [UserRouteAccessService], + }, + { + path: ':id/edit', + component: BloodPressureUpdateComponent, + resolve: { + bloodPressure: BloodPressureRoutingResolveService, + }, + canActivate: [UserRouteAccessService], + }, +]; + +@NgModule({ + imports: [RouterModule.forChild(bloodPressureRoute)], + exports: [RouterModule], +}) +export class BloodPressureRoutingModule {} diff --git a/src/main/webapp/app/entities/blood-pressure/service/blood-pressure.service.spec.ts b/src/main/webapp/app/entities/blood-pressure/service/blood-pressure.service.spec.ts new file mode 100644 index 00000000..09427cdc --- /dev/null +++ b/src/main/webapp/app/entities/blood-pressure/service/blood-pressure.service.spec.ts @@ -0,0 +1,206 @@ +import { TestBed } from '@angular/core/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; + +import { IBloodPressure } from '../blood-pressure.model'; +import { sampleWithRequiredData, sampleWithNewData, sampleWithPartialData, sampleWithFullData } from '../blood-pressure.test-samples'; + +import { BloodPressureService, RestBloodPressure } from './blood-pressure.service'; + +const requireRestSample: RestBloodPressure = { + ...sampleWithRequiredData, + timestamp: sampleWithRequiredData.timestamp?.toJSON(), +}; + +describe('BloodPressure Service', () => { + let service: BloodPressureService; + let httpMock: HttpTestingController; + let expectedResult: IBloodPressure | IBloodPressure[] | boolean | null; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + }); + expectedResult = null; + service = TestBed.inject(BloodPressureService); + httpMock = TestBed.inject(HttpTestingController); + }); + + describe('Service methods', () => { + it('should find an element', () => { + const returnedFromService = { ...requireRestSample }; + const expected = { ...sampleWithRequiredData }; + + service.find(123).subscribe(resp => (expectedResult = resp.body)); + + const req = httpMock.expectOne({ method: 'GET' }); + req.flush(returnedFromService); + expect(expectedResult).toMatchObject(expected); + }); + + it('should create a BloodPressure', () => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const bloodPressure = { ...sampleWithNewData }; + const returnedFromService = { ...requireRestSample }; + const expected = { ...sampleWithRequiredData }; + + service.create(bloodPressure).subscribe(resp => (expectedResult = resp.body)); + + const req = httpMock.expectOne({ method: 'POST' }); + req.flush(returnedFromService); + expect(expectedResult).toMatchObject(expected); + }); + + it('should update a BloodPressure', () => { + const bloodPressure = { ...sampleWithRequiredData }; + const returnedFromService = { ...requireRestSample }; + const expected = { ...sampleWithRequiredData }; + + service.update(bloodPressure).subscribe(resp => (expectedResult = resp.body)); + + const req = httpMock.expectOne({ method: 'PUT' }); + req.flush(returnedFromService); + expect(expectedResult).toMatchObject(expected); + }); + + it('should partial update a BloodPressure', () => { + const patchObject = { ...sampleWithPartialData }; + const returnedFromService = { ...requireRestSample }; + const expected = { ...sampleWithRequiredData }; + + service.partialUpdate(patchObject).subscribe(resp => (expectedResult = resp.body)); + + const req = httpMock.expectOne({ method: 'PATCH' }); + req.flush(returnedFromService); + expect(expectedResult).toMatchObject(expected); + }); + + it('should return a list of BloodPressure', () => { + const returnedFromService = { ...requireRestSample }; + + const expected = { ...sampleWithRequiredData }; + + service.query().subscribe(resp => (expectedResult = resp.body)); + + const req = httpMock.expectOne({ method: 'GET' }); + req.flush([returnedFromService]); + httpMock.verify(); + expect(expectedResult).toMatchObject([expected]); + }); + + it('should delete a BloodPressure', () => { + const expected = true; + + service.delete(123).subscribe(resp => (expectedResult = resp.ok)); + + const req = httpMock.expectOne({ method: 'DELETE' }); + req.flush({ status: 200 }); + expect(expectedResult).toBe(expected); + }); + + describe('addBloodPressureToCollectionIfMissing', () => { + it('should add a BloodPressure to an empty array', () => { + const bloodPressure: IBloodPressure = sampleWithRequiredData; + expectedResult = service.addBloodPressureToCollectionIfMissing([], bloodPressure); + expect(expectedResult).toHaveLength(1); + expect(expectedResult).toContain(bloodPressure); + }); + + it('should not add a BloodPressure to an array that contains it', () => { + const bloodPressure: IBloodPressure = sampleWithRequiredData; + const bloodPressureCollection: IBloodPressure[] = [ + { + ...bloodPressure, + }, + sampleWithPartialData, + ]; + expectedResult = service.addBloodPressureToCollectionIfMissing(bloodPressureCollection, bloodPressure); + expect(expectedResult).toHaveLength(2); + }); + + it("should add a BloodPressure to an array that doesn't contain it", () => { + const bloodPressure: IBloodPressure = sampleWithRequiredData; + const bloodPressureCollection: IBloodPressure[] = [sampleWithPartialData]; + expectedResult = service.addBloodPressureToCollectionIfMissing(bloodPressureCollection, bloodPressure); + expect(expectedResult).toHaveLength(2); + expect(expectedResult).toContain(bloodPressure); + }); + + it('should add only unique BloodPressure to an array', () => { + const bloodPressureArray: IBloodPressure[] = [sampleWithRequiredData, sampleWithPartialData, sampleWithFullData]; + const bloodPressureCollection: IBloodPressure[] = [sampleWithRequiredData]; + expectedResult = service.addBloodPressureToCollectionIfMissing(bloodPressureCollection, ...bloodPressureArray); + expect(expectedResult).toHaveLength(3); + }); + + it('should accept varargs', () => { + const bloodPressure: IBloodPressure = sampleWithRequiredData; + const bloodPressure2: IBloodPressure = sampleWithPartialData; + expectedResult = service.addBloodPressureToCollectionIfMissing([], bloodPressure, bloodPressure2); + expect(expectedResult).toHaveLength(2); + expect(expectedResult).toContain(bloodPressure); + expect(expectedResult).toContain(bloodPressure2); + }); + + it('should accept null and undefined values', () => { + const bloodPressure: IBloodPressure = sampleWithRequiredData; + expectedResult = service.addBloodPressureToCollectionIfMissing([], null, bloodPressure, undefined); + expect(expectedResult).toHaveLength(1); + expect(expectedResult).toContain(bloodPressure); + }); + + it('should return initial array if no BloodPressure is added', () => { + const bloodPressureCollection: IBloodPressure[] = [sampleWithRequiredData]; + expectedResult = service.addBloodPressureToCollectionIfMissing(bloodPressureCollection, undefined, null); + expect(expectedResult).toEqual(bloodPressureCollection); + }); + }); + + describe('compareBloodPressure', () => { + it('Should return true if both entities are null', () => { + const entity1 = null; + const entity2 = null; + + const compareResult = service.compareBloodPressure(entity1, entity2); + + expect(compareResult).toEqual(true); + }); + + it('Should return false if one entity is null', () => { + const entity1 = { id: 123 }; + const entity2 = null; + + const compareResult1 = service.compareBloodPressure(entity1, entity2); + const compareResult2 = service.compareBloodPressure(entity2, entity1); + + expect(compareResult1).toEqual(false); + expect(compareResult2).toEqual(false); + }); + + it('Should return false if primaryKey differs', () => { + const entity1 = { id: 123 }; + const entity2 = { id: 456 }; + + const compareResult1 = service.compareBloodPressure(entity1, entity2); + const compareResult2 = service.compareBloodPressure(entity2, entity1); + + expect(compareResult1).toEqual(false); + expect(compareResult2).toEqual(false); + }); + + it('Should return false if primaryKey matches', () => { + const entity1 = { id: 123 }; + const entity2 = { id: 123 }; + + const compareResult1 = service.compareBloodPressure(entity1, entity2); + const compareResult2 = service.compareBloodPressure(entity2, entity1); + + expect(compareResult1).toEqual(true); + expect(compareResult2).toEqual(true); + }); + }); + }); + + afterEach(() => { + httpMock.verify(); + }); +}); diff --git a/src/main/webapp/app/entities/blood-pressure/service/blood-pressure.service.ts b/src/main/webapp/app/entities/blood-pressure/service/blood-pressure.service.ts new file mode 100644 index 00000000..7dffabd9 --- /dev/null +++ b/src/main/webapp/app/entities/blood-pressure/service/blood-pressure.service.ts @@ -0,0 +1,135 @@ +import { Injectable } from '@angular/core'; +import { HttpClient, HttpResponse } from '@angular/common/http'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; +import dayjs from 'dayjs/esm'; + +import { isPresent } from 'app/core/util/operators'; +import { ApplicationConfigService } from 'app/core/config/application-config.service'; +import { createRequestOption } from 'app/core/request/request-util'; +import { SearchWithPagination } from 'app/core/request/request.model'; +import { IBloodPressure, NewBloodPressure } from '../blood-pressure.model'; + +export type PartialUpdateBloodPressure = Partial & Pick; + +type RestOf = Omit & { + timestamp?: string | null; +}; + +export type RestBloodPressure = RestOf; + +export type NewRestBloodPressure = RestOf; + +export type PartialUpdateRestBloodPressure = RestOf; + +export type EntityResponseType = HttpResponse; +export type EntityArrayResponseType = HttpResponse; + +@Injectable({ providedIn: 'root' }) +export class BloodPressureService { + protected resourceUrl = this.applicationConfigService.getEndpointFor('api/blood-pressures'); + protected resourceSearchUrl = this.applicationConfigService.getEndpointFor('api/_search/blood-pressures'); + + constructor(protected http: HttpClient, protected applicationConfigService: ApplicationConfigService) {} + + create(bloodPressure: NewBloodPressure): Observable { + const copy = this.convertDateFromClient(bloodPressure); + return this.http + .post(this.resourceUrl, copy, { observe: 'response' }) + .pipe(map(res => this.convertResponseFromServer(res))); + } + + update(bloodPressure: IBloodPressure): Observable { + const copy = this.convertDateFromClient(bloodPressure); + return this.http + .put(`${this.resourceUrl}/${this.getBloodPressureIdentifier(bloodPressure)}`, copy, { observe: 'response' }) + .pipe(map(res => this.convertResponseFromServer(res))); + } + + partialUpdate(bloodPressure: PartialUpdateBloodPressure): Observable { + const copy = this.convertDateFromClient(bloodPressure); + return this.http + .patch(`${this.resourceUrl}/${this.getBloodPressureIdentifier(bloodPressure)}`, copy, { observe: 'response' }) + .pipe(map(res => this.convertResponseFromServer(res))); + } + + find(id: number): Observable { + return this.http + .get(`${this.resourceUrl}/${id}`, { observe: 'response' }) + .pipe(map(res => this.convertResponseFromServer(res))); + } + + query(req?: any): Observable { + const options = createRequestOption(req); + return this.http + .get(this.resourceUrl, { params: options, observe: 'response' }) + .pipe(map(res => this.convertResponseArrayFromServer(res))); + } + + delete(id: number): Observable> { + return this.http.delete(`${this.resourceUrl}/${id}`, { observe: 'response' }); + } + + search(req: SearchWithPagination): Observable { + const options = createRequestOption(req); + return this.http + .get(this.resourceSearchUrl, { params: options, observe: 'response' }) + .pipe(map(res => this.convertResponseArrayFromServer(res))); + } + + getBloodPressureIdentifier(bloodPressure: Pick): number { + return bloodPressure.id; + } + + compareBloodPressure(o1: Pick | null, o2: Pick | null): boolean { + return o1 && o2 ? this.getBloodPressureIdentifier(o1) === this.getBloodPressureIdentifier(o2) : o1 === o2; + } + + addBloodPressureToCollectionIfMissing>( + bloodPressureCollection: Type[], + ...bloodPressuresToCheck: (Type | null | undefined)[] + ): Type[] { + const bloodPressures: Type[] = bloodPressuresToCheck.filter(isPresent); + if (bloodPressures.length > 0) { + const bloodPressureCollectionIdentifiers = bloodPressureCollection.map( + bloodPressureItem => this.getBloodPressureIdentifier(bloodPressureItem)! + ); + const bloodPressuresToAdd = bloodPressures.filter(bloodPressureItem => { + const bloodPressureIdentifier = this.getBloodPressureIdentifier(bloodPressureItem); + if (bloodPressureCollectionIdentifiers.includes(bloodPressureIdentifier)) { + return false; + } + bloodPressureCollectionIdentifiers.push(bloodPressureIdentifier); + return true; + }); + return [...bloodPressuresToAdd, ...bloodPressureCollection]; + } + return bloodPressureCollection; + } + + protected convertDateFromClient(bloodPressure: T): RestOf { + return { + ...bloodPressure, + timestamp: bloodPressure.timestamp?.toJSON() ?? null, + }; + } + + protected convertDateFromServer(restBloodPressure: RestBloodPressure): IBloodPressure { + return { + ...restBloodPressure, + timestamp: restBloodPressure.timestamp ? dayjs(restBloodPressure.timestamp) : undefined, + }; + } + + protected convertResponseFromServer(res: HttpResponse): HttpResponse { + return res.clone({ + body: res.body ? this.convertDateFromServer(res.body) : null, + }); + } + + protected convertResponseArrayFromServer(res: HttpResponse): HttpResponse { + return res.clone({ + body: res.body ? res.body.map(item => this.convertDateFromServer(item)) : null, + }); + } +} diff --git a/src/main/webapp/app/entities/blood-pressure/update/blood-pressure-form.service.spec.ts b/src/main/webapp/app/entities/blood-pressure/update/blood-pressure-form.service.spec.ts new file mode 100644 index 00000000..e400432e --- /dev/null +++ b/src/main/webapp/app/entities/blood-pressure/update/blood-pressure-form.service.spec.ts @@ -0,0 +1,93 @@ +import { TestBed } from '@angular/core/testing'; + +import { sampleWithRequiredData, sampleWithNewData } from '../blood-pressure.test-samples'; + +import { BloodPressureFormService } from './blood-pressure-form.service'; + +describe('BloodPressure Form Service', () => { + let service: BloodPressureFormService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(BloodPressureFormService); + }); + + describe('Service methods', () => { + describe('createBloodPressureFormGroup', () => { + it('should create a new form with FormControl', () => { + const formGroup = service.createBloodPressureFormGroup(); + + expect(formGroup.controls).toEqual( + expect.objectContaining({ + id: expect.any(Object), + timestamp: expect.any(Object), + systolic: expect.any(Object), + diastolic: expect.any(Object), + user: expect.any(Object), + }) + ); + }); + + it('passing IBloodPressure should create a new form with FormGroup', () => { + const formGroup = service.createBloodPressureFormGroup(sampleWithRequiredData); + + expect(formGroup.controls).toEqual( + expect.objectContaining({ + id: expect.any(Object), + timestamp: expect.any(Object), + systolic: expect.any(Object), + diastolic: expect.any(Object), + user: expect.any(Object), + }) + ); + }); + }); + + describe('getBloodPressure', () => { + it('should return NewBloodPressure for default BloodPressure initial value', () => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const formGroup = service.createBloodPressureFormGroup(sampleWithNewData); + + const bloodPressure = service.getBloodPressure(formGroup) as any; + + expect(bloodPressure).toMatchObject(sampleWithNewData); + }); + + it('should return NewBloodPressure for empty BloodPressure initial value', () => { + const formGroup = service.createBloodPressureFormGroup(); + + const bloodPressure = service.getBloodPressure(formGroup) as any; + + expect(bloodPressure).toMatchObject({}); + }); + + it('should return IBloodPressure', () => { + const formGroup = service.createBloodPressureFormGroup(sampleWithRequiredData); + + const bloodPressure = service.getBloodPressure(formGroup) as any; + + expect(bloodPressure).toMatchObject(sampleWithRequiredData); + }); + }); + + describe('resetForm', () => { + it('passing IBloodPressure should not enable id FormControl', () => { + const formGroup = service.createBloodPressureFormGroup(); + expect(formGroup.controls.id.disabled).toBe(true); + + service.resetForm(formGroup, sampleWithRequiredData); + + expect(formGroup.controls.id.disabled).toBe(true); + }); + + it('passing NewBloodPressure should disable id FormControl', () => { + const formGroup = service.createBloodPressureFormGroup(sampleWithRequiredData); + expect(formGroup.controls.id.disabled).toBe(true); + + service.resetForm(formGroup, { id: null }); + + expect(formGroup.controls.id.disabled).toBe(true); + }); + }); + }); +}); diff --git a/src/main/webapp/app/entities/blood-pressure/update/blood-pressure-form.service.ts b/src/main/webapp/app/entities/blood-pressure/update/blood-pressure-form.service.ts new file mode 100644 index 00000000..01f0689b --- /dev/null +++ b/src/main/webapp/app/entities/blood-pressure/update/blood-pressure-form.service.ts @@ -0,0 +1,110 @@ +import { Injectable } from '@angular/core'; +import { FormGroup, FormControl, Validators } from '@angular/forms'; + +import dayjs from 'dayjs/esm'; +import { DATE_TIME_FORMAT } from 'app/config/input.constants'; +import { IBloodPressure, NewBloodPressure } from '../blood-pressure.model'; + +/** + * A partial Type with required key is used as form input. + */ +type PartialWithRequiredKeyOf = Partial> & { id: T['id'] }; + +/** + * Type for createFormGroup and resetForm argument. + * It accepts IBloodPressure for edit and NewBloodPressureFormGroupInput for create. + */ +type BloodPressureFormGroupInput = IBloodPressure | PartialWithRequiredKeyOf; + +/** + * Type that converts some properties for forms. + */ +type FormValueOf = Omit & { + timestamp?: string | null; +}; + +type BloodPressureFormRawValue = FormValueOf; + +type NewBloodPressureFormRawValue = FormValueOf; + +type BloodPressureFormDefaults = Pick; + +type BloodPressureFormGroupContent = { + id: FormControl; + timestamp: FormControl; + systolic: FormControl; + diastolic: FormControl; + user: FormControl; +}; + +export type BloodPressureFormGroup = FormGroup; + +@Injectable({ providedIn: 'root' }) +export class BloodPressureFormService { + createBloodPressureFormGroup(bloodPressure: BloodPressureFormGroupInput = { id: null }): BloodPressureFormGroup { + const bloodPressureRawValue = this.convertBloodPressureToBloodPressureRawValue({ + ...this.getFormDefaults(), + ...bloodPressure, + }); + return new FormGroup({ + id: new FormControl( + { value: bloodPressureRawValue.id, disabled: true }, + { + nonNullable: true, + validators: [Validators.required], + } + ), + timestamp: new FormControl(bloodPressureRawValue.timestamp, { + validators: [Validators.required], + }), + systolic: new FormControl(bloodPressureRawValue.systolic, { + validators: [Validators.required], + }), + diastolic: new FormControl(bloodPressureRawValue.diastolic, { + validators: [Validators.required], + }), + user: new FormControl(bloodPressureRawValue.user), + }); + } + + getBloodPressure(form: BloodPressureFormGroup): IBloodPressure | NewBloodPressure { + return this.convertBloodPressureRawValueToBloodPressure(form.getRawValue() as BloodPressureFormRawValue | NewBloodPressureFormRawValue); + } + + resetForm(form: BloodPressureFormGroup, bloodPressure: BloodPressureFormGroupInput): void { + const bloodPressureRawValue = this.convertBloodPressureToBloodPressureRawValue({ ...this.getFormDefaults(), ...bloodPressure }); + form.reset( + { + ...bloodPressureRawValue, + id: { value: bloodPressureRawValue.id, disabled: true }, + } as any /* cast to workaround https://github.com/angular/angular/issues/46458 */ + ); + } + + private getFormDefaults(): BloodPressureFormDefaults { + const currentTime = dayjs(); + + return { + id: null, + timestamp: currentTime, + }; + } + + private convertBloodPressureRawValueToBloodPressure( + rawBloodPressure: BloodPressureFormRawValue | NewBloodPressureFormRawValue + ): IBloodPressure | NewBloodPressure { + return { + ...rawBloodPressure, + timestamp: dayjs(rawBloodPressure.timestamp, DATE_TIME_FORMAT), + }; + } + + private convertBloodPressureToBloodPressureRawValue( + bloodPressure: IBloodPressure | (Partial & BloodPressureFormDefaults) + ): BloodPressureFormRawValue | PartialWithRequiredKeyOf { + return { + ...bloodPressure, + timestamp: bloodPressure.timestamp ? bloodPressure.timestamp.format(DATE_TIME_FORMAT) : undefined, + }; + } +} diff --git a/src/main/webapp/app/entities/blood-pressure/update/blood-pressure-update.component.html b/src/main/webapp/app/entities/blood-pressure/update/blood-pressure-update.component.html new file mode 100644 index 00000000..6f43985e --- /dev/null +++ b/src/main/webapp/app/entities/blood-pressure/update/blood-pressure-update.component.html @@ -0,0 +1,119 @@ +
+
+
+

+ Create or edit a Blood Pressure +

+ +
+ + +
+ + +
+ +
+ +
+ +
+
+ + This field is required. + + + This field should be a date and time. + +
+
+ +
+ + +
+ + This field is required. + + + This field should be a number. + +
+
+ +
+ + +
+ + This field is required. + + + This field should be a number. + +
+
+ +
+ + +
+
+ +
+ + + +
+
+
+
diff --git a/src/main/webapp/app/entities/blood-pressure/update/blood-pressure-update.component.spec.ts b/src/main/webapp/app/entities/blood-pressure/update/blood-pressure-update.component.spec.ts new file mode 100644 index 00000000..9a98f8c1 --- /dev/null +++ b/src/main/webapp/app/entities/blood-pressure/update/blood-pressure-update.component.spec.ts @@ -0,0 +1,167 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { HttpResponse } from '@angular/common/http'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { FormBuilder } from '@angular/forms'; +import { ActivatedRoute } from '@angular/router'; +import { RouterTestingModule } from '@angular/router/testing'; +import { of, Subject, from } from 'rxjs'; + +import { BloodPressureFormService } from './blood-pressure-form.service'; +import { BloodPressureService } from '../service/blood-pressure.service'; +import { IBloodPressure } from '../blood-pressure.model'; + +import { IUser } from 'app/entities/user/user.model'; +import { UserService } from 'app/entities/user/user.service'; + +import { BloodPressureUpdateComponent } from './blood-pressure-update.component'; + +describe('BloodPressure Management Update Component', () => { + let comp: BloodPressureUpdateComponent; + let fixture: ComponentFixture; + let activatedRoute: ActivatedRoute; + let bloodPressureFormService: BloodPressureFormService; + let bloodPressureService: BloodPressureService; + let userService: UserService; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule, RouterTestingModule.withRoutes([])], + declarations: [BloodPressureUpdateComponent], + providers: [ + FormBuilder, + { + provide: ActivatedRoute, + useValue: { + params: from([{}]), + }, + }, + ], + }) + .overrideTemplate(BloodPressureUpdateComponent, '') + .compileComponents(); + + fixture = TestBed.createComponent(BloodPressureUpdateComponent); + activatedRoute = TestBed.inject(ActivatedRoute); + bloodPressureFormService = TestBed.inject(BloodPressureFormService); + bloodPressureService = TestBed.inject(BloodPressureService); + userService = TestBed.inject(UserService); + + comp = fixture.componentInstance; + }); + + describe('ngOnInit', () => { + it('Should call User query and add missing value', () => { + const bloodPressure: IBloodPressure = { id: 456 }; + const user: IUser = { id: 3909 }; + bloodPressure.user = user; + + const userCollection: IUser[] = [{ id: 46631 }]; + jest.spyOn(userService, 'query').mockReturnValue(of(new HttpResponse({ body: userCollection }))); + const additionalUsers = [user]; + const expectedCollection: IUser[] = [...additionalUsers, ...userCollection]; + jest.spyOn(userService, 'addUserToCollectionIfMissing').mockReturnValue(expectedCollection); + + activatedRoute.data = of({ bloodPressure }); + comp.ngOnInit(); + + expect(userService.query).toHaveBeenCalled(); + expect(userService.addUserToCollectionIfMissing).toHaveBeenCalledWith( + userCollection, + ...additionalUsers.map(expect.objectContaining) + ); + expect(comp.usersSharedCollection).toEqual(expectedCollection); + }); + + it('Should update editForm', () => { + const bloodPressure: IBloodPressure = { id: 456 }; + const user: IUser = { id: 72703 }; + bloodPressure.user = user; + + activatedRoute.data = of({ bloodPressure }); + comp.ngOnInit(); + + expect(comp.usersSharedCollection).toContain(user); + expect(comp.bloodPressure).toEqual(bloodPressure); + }); + }); + + describe('save', () => { + it('Should call update service on save for existing entity', () => { + // GIVEN + const saveSubject = new Subject>(); + const bloodPressure = { id: 123 }; + jest.spyOn(bloodPressureFormService, 'getBloodPressure').mockReturnValue(bloodPressure); + jest.spyOn(bloodPressureService, 'update').mockReturnValue(saveSubject); + jest.spyOn(comp, 'previousState'); + activatedRoute.data = of({ bloodPressure }); + comp.ngOnInit(); + + // WHEN + comp.save(); + expect(comp.isSaving).toEqual(true); + saveSubject.next(new HttpResponse({ body: bloodPressure })); + saveSubject.complete(); + + // THEN + expect(bloodPressureFormService.getBloodPressure).toHaveBeenCalled(); + expect(comp.previousState).toHaveBeenCalled(); + expect(bloodPressureService.update).toHaveBeenCalledWith(expect.objectContaining(bloodPressure)); + expect(comp.isSaving).toEqual(false); + }); + + it('Should call create service on save for new entity', () => { + // GIVEN + const saveSubject = new Subject>(); + const bloodPressure = { id: 123 }; + jest.spyOn(bloodPressureFormService, 'getBloodPressure').mockReturnValue({ id: null }); + jest.spyOn(bloodPressureService, 'create').mockReturnValue(saveSubject); + jest.spyOn(comp, 'previousState'); + activatedRoute.data = of({ bloodPressure: null }); + comp.ngOnInit(); + + // WHEN + comp.save(); + expect(comp.isSaving).toEqual(true); + saveSubject.next(new HttpResponse({ body: bloodPressure })); + saveSubject.complete(); + + // THEN + expect(bloodPressureFormService.getBloodPressure).toHaveBeenCalled(); + expect(bloodPressureService.create).toHaveBeenCalled(); + expect(comp.isSaving).toEqual(false); + expect(comp.previousState).toHaveBeenCalled(); + }); + + it('Should set isSaving to false on error', () => { + // GIVEN + const saveSubject = new Subject>(); + const bloodPressure = { id: 123 }; + jest.spyOn(bloodPressureService, 'update').mockReturnValue(saveSubject); + jest.spyOn(comp, 'previousState'); + activatedRoute.data = of({ bloodPressure }); + comp.ngOnInit(); + + // WHEN + comp.save(); + expect(comp.isSaving).toEqual(true); + saveSubject.error('This is an error!'); + + // THEN + expect(bloodPressureService.update).toHaveBeenCalled(); + expect(comp.isSaving).toEqual(false); + expect(comp.previousState).not.toHaveBeenCalled(); + }); + }); + + describe('Compare relationships', () => { + describe('compareUser', () => { + it('Should forward to userService', () => { + const entity = { id: 123 }; + const entity2 = { id: 456 }; + jest.spyOn(userService, 'compareUser'); + comp.compareUser(entity, entity2); + expect(userService.compareUser).toHaveBeenCalledWith(entity, entity2); + }); + }); + }); +}); diff --git a/src/main/webapp/app/entities/blood-pressure/update/blood-pressure-update.component.ts b/src/main/webapp/app/entities/blood-pressure/update/blood-pressure-update.component.ts new file mode 100644 index 00000000..20c19935 --- /dev/null +++ b/src/main/webapp/app/entities/blood-pressure/update/blood-pressure-update.component.ts @@ -0,0 +1,92 @@ +import { Component, OnInit } from '@angular/core'; +import { HttpResponse } from '@angular/common/http'; +import { ActivatedRoute } from '@angular/router'; +import { Observable } from 'rxjs'; +import { finalize, map } from 'rxjs/operators'; + +import { BloodPressureFormService, BloodPressureFormGroup } from './blood-pressure-form.service'; +import { IBloodPressure } from '../blood-pressure.model'; +import { BloodPressureService } from '../service/blood-pressure.service'; +import { IUser } from 'app/entities/user/user.model'; +import { UserService } from 'app/entities/user/user.service'; + +@Component({ + selector: 'jhi-blood-pressure-update', + templateUrl: './blood-pressure-update.component.html', +}) +export class BloodPressureUpdateComponent implements OnInit { + isSaving = false; + bloodPressure: IBloodPressure | null = null; + + usersSharedCollection: IUser[] = []; + + editForm: BloodPressureFormGroup = this.bloodPressureFormService.createBloodPressureFormGroup(); + + constructor( + protected bloodPressureService: BloodPressureService, + protected bloodPressureFormService: BloodPressureFormService, + protected userService: UserService, + protected activatedRoute: ActivatedRoute + ) {} + + compareUser = (o1: IUser | null, o2: IUser | null): boolean => this.userService.compareUser(o1, o2); + + ngOnInit(): void { + this.activatedRoute.data.subscribe(({ bloodPressure }) => { + this.bloodPressure = bloodPressure; + if (bloodPressure) { + this.updateForm(bloodPressure); + } + + this.loadRelationshipsOptions(); + }); + } + + previousState(): void { + window.history.back(); + } + + save(): void { + this.isSaving = true; + const bloodPressure = this.bloodPressureFormService.getBloodPressure(this.editForm); + if (bloodPressure.id !== null) { + this.subscribeToSaveResponse(this.bloodPressureService.update(bloodPressure)); + } else { + this.subscribeToSaveResponse(this.bloodPressureService.create(bloodPressure)); + } + } + + protected subscribeToSaveResponse(result: Observable>): void { + result.pipe(finalize(() => this.onSaveFinalize())).subscribe({ + next: () => this.onSaveSuccess(), + error: () => this.onSaveError(), + }); + } + + protected onSaveSuccess(): void { + this.previousState(); + } + + protected onSaveError(): void { + // Api for inheritance. + } + + protected onSaveFinalize(): void { + this.isSaving = false; + } + + protected updateForm(bloodPressure: IBloodPressure): void { + this.bloodPressure = bloodPressure; + this.bloodPressureFormService.resetForm(this.editForm, bloodPressure); + + this.usersSharedCollection = this.userService.addUserToCollectionIfMissing(this.usersSharedCollection, bloodPressure.user); + } + + protected loadRelationshipsOptions(): void { + this.userService + .query() + .pipe(map((res: HttpResponse) => res.body ?? [])) + .pipe(map((users: IUser[]) => this.userService.addUserToCollectionIfMissing(users, this.bloodPressure?.user))) + .subscribe((users: IUser[]) => (this.usersSharedCollection = users)); + } +} diff --git a/src/main/webapp/app/entities/entity-navbar-items.ts b/src/main/webapp/app/entities/entity-navbar-items.ts new file mode 100644 index 00000000..d96c572b --- /dev/null +++ b/src/main/webapp/app/entities/entity-navbar-items.ts @@ -0,0 +1,22 @@ +export const EntityNavbarItems = [ + { + name: 'Points', + route: 'points', + translationKey: 'global.menu.entities.points', + }, + { + name: 'Weight', + route: 'weight', + translationKey: 'global.menu.entities.weight', + }, + { + name: 'BloodPressure', + route: 'blood-pressure', + translationKey: 'global.menu.entities.bloodPressure', + }, + { + name: 'Preferences', + route: 'preferences', + translationKey: 'global.menu.entities.preferences', + }, +]; diff --git a/src/main/webapp/app/entities/entity-routing.module.ts b/src/main/webapp/app/entities/entity-routing.module.ts new file mode 100644 index 00000000..80f42680 --- /dev/null +++ b/src/main/webapp/app/entities/entity-routing.module.ts @@ -0,0 +1,31 @@ +import { NgModule } from '@angular/core'; +import { RouterModule } from '@angular/router'; + +@NgModule({ + imports: [ + RouterModule.forChild([ + { + path: 'points', + data: { pageTitle: 'twentyOnePointsApp.points.home.title' }, + loadChildren: () => import('./points/points.module').then(m => m.PointsModule), + }, + { + path: 'weight', + data: { pageTitle: 'twentyOnePointsApp.weight.home.title' }, + loadChildren: () => import('./weight/weight.module').then(m => m.WeightModule), + }, + { + path: 'blood-pressure', + data: { pageTitle: 'twentyOnePointsApp.bloodPressure.home.title' }, + loadChildren: () => import('./blood-pressure/blood-pressure.module').then(m => m.BloodPressureModule), + }, + { + path: 'preferences', + data: { pageTitle: 'twentyOnePointsApp.preferences.home.title' }, + loadChildren: () => import('./preferences/preferences.module').then(m => m.PreferencesModule), + }, + /* jhipster-needle-add-entity-route - JHipster will add entity modules routes here */ + ]), + ], +}) +export class EntityRoutingModule {} diff --git a/src/main/webapp/app/entities/enumerations/units.model.ts b/src/main/webapp/app/entities/enumerations/units.model.ts new file mode 100644 index 00000000..cd22e341 --- /dev/null +++ b/src/main/webapp/app/entities/enumerations/units.model.ts @@ -0,0 +1,5 @@ +export enum Units { + KG = 'KG', + + LB = 'LB', +} diff --git a/src/main/webapp/app/entities/points/delete/points-delete-dialog.component.html b/src/main/webapp/app/entities/points/delete/points-delete-dialog.component.html new file mode 100644 index 00000000..fdc2e6e6 --- /dev/null +++ b/src/main/webapp/app/entities/points/delete/points-delete-dialog.component.html @@ -0,0 +1,24 @@ +
+ + + + + +
diff --git a/src/main/webapp/app/entities/points/delete/points-delete-dialog.component.spec.ts b/src/main/webapp/app/entities/points/delete/points-delete-dialog.component.spec.ts new file mode 100644 index 00000000..146fc504 --- /dev/null +++ b/src/main/webapp/app/entities/points/delete/points-delete-dialog.component.spec.ts @@ -0,0 +1,63 @@ +jest.mock('@ng-bootstrap/ng-bootstrap'); + +import { ComponentFixture, TestBed, inject, fakeAsync, tick } from '@angular/core/testing'; +import { HttpResponse } from '@angular/common/http'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { of } from 'rxjs'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; + +import { PointsService } from '../service/points.service'; + +import { PointsDeleteDialogComponent } from './points-delete-dialog.component'; + +describe('Points Management Delete Component', () => { + let comp: PointsDeleteDialogComponent; + let fixture: ComponentFixture; + let service: PointsService; + let mockActiveModal: NgbActiveModal; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + declarations: [PointsDeleteDialogComponent], + providers: [NgbActiveModal], + }) + .overrideTemplate(PointsDeleteDialogComponent, '') + .compileComponents(); + fixture = TestBed.createComponent(PointsDeleteDialogComponent); + comp = fixture.componentInstance; + service = TestBed.inject(PointsService); + mockActiveModal = TestBed.inject(NgbActiveModal); + }); + + describe('confirmDelete', () => { + it('Should call delete service on confirmDelete', inject( + [], + fakeAsync(() => { + // GIVEN + jest.spyOn(service, 'delete').mockReturnValue(of(new HttpResponse({ body: {} }))); + + // WHEN + comp.confirmDelete(123); + tick(); + + // THEN + expect(service.delete).toHaveBeenCalledWith(123); + expect(mockActiveModal.close).toHaveBeenCalledWith('deleted'); + }) + )); + + it('Should not call delete service on clear', () => { + // GIVEN + jest.spyOn(service, 'delete'); + + // WHEN + comp.cancel(); + + // THEN + expect(service.delete).not.toHaveBeenCalled(); + expect(mockActiveModal.close).not.toHaveBeenCalled(); + expect(mockActiveModal.dismiss).toHaveBeenCalled(); + }); + }); +}); diff --git a/src/main/webapp/app/entities/points/delete/points-delete-dialog.component.ts b/src/main/webapp/app/entities/points/delete/points-delete-dialog.component.ts new file mode 100644 index 00000000..416c5bcb --- /dev/null +++ b/src/main/webapp/app/entities/points/delete/points-delete-dialog.component.ts @@ -0,0 +1,25 @@ +import { Component } from '@angular/core'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; + +import { IPoints } from '../points.model'; +import { PointsService } from '../service/points.service'; +import { ITEM_DELETED_EVENT } from 'app/config/navigation.constants'; + +@Component({ + templateUrl: './points-delete-dialog.component.html', +}) +export class PointsDeleteDialogComponent { + points?: IPoints; + + constructor(protected pointsService: PointsService, protected activeModal: NgbActiveModal) {} + + cancel(): void { + this.activeModal.dismiss(); + } + + confirmDelete(id: number): void { + this.pointsService.delete(id).subscribe(() => { + this.activeModal.close(ITEM_DELETED_EVENT); + }); + } +} diff --git a/src/main/webapp/app/entities/points/detail/points-detail.component.html b/src/main/webapp/app/entities/points/detail/points-detail.component.html new file mode 100644 index 00000000..4546c3c7 --- /dev/null +++ b/src/main/webapp/app/entities/points/detail/points-detail.component.html @@ -0,0 +1,52 @@ +
+
+
+

Points

+ +
+ + + + + +
+
ID
+
+ {{ points.id }} +
+
Date
+
+ {{ points.date | formatMediumDate }} +
+
Exercise
+
+ {{ points.exercise }} +
+
Meals
+
+ {{ points.meals }} +
+
Alcohol
+
+ {{ points.alcohol }} +
+
Notes
+
+ {{ points.notes }} +
+
User
+
+ {{ points.user?.login }} +
+
+ + + + +
+
+
diff --git a/src/main/webapp/app/entities/points/detail/points-detail.component.spec.ts b/src/main/webapp/app/entities/points/detail/points-detail.component.spec.ts new file mode 100644 index 00000000..c18b1a18 --- /dev/null +++ b/src/main/webapp/app/entities/points/detail/points-detail.component.spec.ts @@ -0,0 +1,36 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ActivatedRoute } from '@angular/router'; +import { of } from 'rxjs'; + +import { PointsDetailComponent } from './points-detail.component'; + +describe('Points Management Detail Component', () => { + let comp: PointsDetailComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [PointsDetailComponent], + providers: [ + { + provide: ActivatedRoute, + useValue: { data: of({ points: { id: 123 } }) }, + }, + ], + }) + .overrideTemplate(PointsDetailComponent, '') + .compileComponents(); + fixture = TestBed.createComponent(PointsDetailComponent); + comp = fixture.componentInstance; + }); + + describe('OnInit', () => { + it('Should load points on init', () => { + // WHEN + comp.ngOnInit(); + + // THEN + expect(comp.points).toEqual(expect.objectContaining({ id: 123 })); + }); + }); +}); diff --git a/src/main/webapp/app/entities/points/detail/points-detail.component.ts b/src/main/webapp/app/entities/points/detail/points-detail.component.ts new file mode 100644 index 00000000..b4f4297b --- /dev/null +++ b/src/main/webapp/app/entities/points/detail/points-detail.component.ts @@ -0,0 +1,24 @@ +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; + +import { IPoints } from '../points.model'; + +@Component({ + selector: 'jhi-points-detail', + templateUrl: './points-detail.component.html', +}) +export class PointsDetailComponent implements OnInit { + points: IPoints | null = null; + + constructor(protected activatedRoute: ActivatedRoute) {} + + ngOnInit(): void { + this.activatedRoute.data.subscribe(({ points }) => { + this.points = points; + }); + } + + previousState(): void { + window.history.back(); + } +} diff --git a/src/main/webapp/app/entities/points/list/points.component.html b/src/main/webapp/app/entities/points/list/points.component.html new file mode 100644 index 00000000..ca40564c --- /dev/null +++ b/src/main/webapp/app/entities/points/list/points.component.html @@ -0,0 +1,157 @@ +
+

+ Points + +
+ + + +
+

+ + + + + +
+
+
+ + + + + + +
+
+
+ +
+ No Points found +
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ ID + +
+
+
+ Date + +
+
+
+ Exercise + +
+
+
+ Meals + +
+
+
+ Alcohol + +
+
+
+ Notes + +
+
+
+ User + +
+
+ {{ points.id }} + {{ points.date | formatMediumDate }}{{ points.exercise }}{{ points.meals }}{{ points.alcohol }}{{ points.notes }} + {{ points.user?.login }} + +
+ + + + + +
+
+
+ +
+
+ +
+ +
+ +
+
+
diff --git a/src/main/webapp/app/entities/points/list/points.component.spec.ts b/src/main/webapp/app/entities/points/list/points.component.spec.ts new file mode 100644 index 00000000..f2cc82f3 --- /dev/null +++ b/src/main/webapp/app/entities/points/list/points.component.spec.ts @@ -0,0 +1,113 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { HttpHeaders, HttpResponse } from '@angular/common/http'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { ActivatedRoute } from '@angular/router'; +import { RouterTestingModule } from '@angular/router/testing'; +import { of } from 'rxjs'; + +import { PointsService } from '../service/points.service'; + +import { PointsComponent } from './points.component'; +import SpyInstance = jest.SpyInstance; + +describe('Points Management Component', () => { + let comp: PointsComponent; + let fixture: ComponentFixture; + let service: PointsService; + let routerNavigateSpy: SpyInstance>; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [RouterTestingModule.withRoutes([{ path: 'points', component: PointsComponent }]), HttpClientTestingModule], + declarations: [PointsComponent], + providers: [ + { + provide: ActivatedRoute, + useValue: { + data: of({ + defaultSort: 'id,asc', + }), + queryParamMap: of( + jest.requireActual('@angular/router').convertToParamMap({ + page: '1', + size: '1', + sort: 'id,desc', + }) + ), + snapshot: { queryParams: {} }, + }, + }, + ], + }) + .overrideTemplate(PointsComponent, '') + .compileComponents(); + + fixture = TestBed.createComponent(PointsComponent); + comp = fixture.componentInstance; + service = TestBed.inject(PointsService); + routerNavigateSpy = jest.spyOn(comp.router, 'navigate'); + + const headers = new HttpHeaders(); + jest.spyOn(service, 'query').mockReturnValue( + of( + new HttpResponse({ + body: [{ id: 123 }], + headers, + }) + ) + ); + }); + + it('Should call load all on init', () => { + // WHEN + comp.ngOnInit(); + + // THEN + expect(service.query).toHaveBeenCalled(); + expect(comp.points?.[0]).toEqual(expect.objectContaining({ id: 123 })); + }); + + describe('trackId', () => { + it('Should forward to pointsService', () => { + const entity = { id: 123 }; + jest.spyOn(service, 'getPointsIdentifier'); + const id = comp.trackId(0, entity); + expect(service.getPointsIdentifier).toHaveBeenCalledWith(entity); + expect(id).toBe(entity.id); + }); + }); + + it('should load a page', () => { + // WHEN + comp.navigateToPage(1); + + // THEN + expect(routerNavigateSpy).toHaveBeenCalled(); + }); + + it('should calculate the sort attribute for an id', () => { + // WHEN + comp.ngOnInit(); + + // THEN + expect(service.query).toHaveBeenLastCalledWith(expect.objectContaining({ sort: ['id,desc'] })); + }); + + it('should calculate the sort attribute for a non-id attribute', () => { + // GIVEN + comp.predicate = 'name'; + + // WHEN + comp.navigateToWithComponentValues(); + + // THEN + expect(routerNavigateSpy).toHaveBeenLastCalledWith( + expect.anything(), + expect.objectContaining({ + queryParams: expect.objectContaining({ + sort: ['name,asc'], + }), + }) + ); + }); +}); diff --git a/src/main/webapp/app/entities/points/list/points.component.ts b/src/main/webapp/app/entities/points/list/points.component.ts new file mode 100644 index 00000000..9d5290e0 --- /dev/null +++ b/src/main/webapp/app/entities/points/list/points.component.ts @@ -0,0 +1,166 @@ +import { Component, OnInit } from '@angular/core'; +import { HttpHeaders } from '@angular/common/http'; +import { ActivatedRoute, Data, ParamMap, Router } from '@angular/router'; +import { combineLatest, filter, Observable, switchMap, tap } from 'rxjs'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; + +import { IPoints } from '../points.model'; + +import { ITEMS_PER_PAGE, PAGE_HEADER, TOTAL_COUNT_RESPONSE_HEADER } from 'app/config/pagination.constants'; +import { ASC, DESC, SORT, ITEM_DELETED_EVENT, DEFAULT_SORT_DATA } from 'app/config/navigation.constants'; +import { EntityArrayResponseType, PointsService } from '../service/points.service'; +import { PointsDeleteDialogComponent } from '../delete/points-delete-dialog.component'; + +@Component({ + selector: 'jhi-points', + templateUrl: './points.component.html', +}) +export class PointsComponent implements OnInit { + private static readonly NOT_SORTABLE_FIELDS_AFTER_SEARCH = ['notes']; + + points?: IPoints[]; + isLoading = false; + + predicate = 'id'; + ascending = true; + currentSearch = ''; + + itemsPerPage = ITEMS_PER_PAGE; + totalItems = 0; + page = 1; + + constructor( + protected pointsService: PointsService, + protected activatedRoute: ActivatedRoute, + public router: Router, + protected modalService: NgbModal + ) {} + + trackId = (_index: number, item: IPoints): number => this.pointsService.getPointsIdentifier(item); + + search(query: string): void { + if (query && PointsComponent.NOT_SORTABLE_FIELDS_AFTER_SEARCH.includes(this.predicate)) { + this.predicate = 'id'; + this.ascending = true; + } + this.page = 1; + this.currentSearch = query; + this.navigateToWithComponentValues(); + } + + ngOnInit(): void { + this.load(); + } + + delete(points: IPoints): void { + const modalRef = this.modalService.open(PointsDeleteDialogComponent, { size: 'lg', backdrop: 'static' }); + modalRef.componentInstance.points = points; + // unsubscribe not needed because closed completes on modal close + modalRef.closed + .pipe( + filter(reason => reason === ITEM_DELETED_EVENT), + switchMap(() => this.loadFromBackendWithRouteInformations()) + ) + .subscribe({ + next: (res: EntityArrayResponseType) => { + this.onResponseSuccess(res); + }, + }); + } + + load(): void { + this.loadFromBackendWithRouteInformations().subscribe({ + next: (res: EntityArrayResponseType) => { + this.onResponseSuccess(res); + }, + }); + } + + navigateToWithComponentValues(): void { + this.handleNavigation(this.page, this.predicate, this.ascending, this.currentSearch); + } + + navigateToPage(page = this.page): void { + this.handleNavigation(page, this.predicate, this.ascending, this.currentSearch); + } + + protected loadFromBackendWithRouteInformations(): Observable { + return combineLatest([this.activatedRoute.queryParamMap, this.activatedRoute.data]).pipe( + tap(([params, data]) => this.fillComponentAttributeFromRoute(params, data)), + switchMap(() => this.queryBackend(this.page, this.predicate, this.ascending, this.currentSearch)) + ); + } + + protected fillComponentAttributeFromRoute(params: ParamMap, data: Data): void { + const page = params.get(PAGE_HEADER); + this.page = +(page ?? 1); + const sort = (params.get(SORT) ?? data[DEFAULT_SORT_DATA]).split(','); + this.predicate = sort[0]; + this.ascending = sort[1] === ASC; + if (params.has('search') && params.get('search') !== '') { + this.currentSearch = params.get('search') as string; + if (PointsComponent.NOT_SORTABLE_FIELDS_AFTER_SEARCH.includes(this.predicate)) { + this.predicate = ''; + } + } + } + + protected onResponseSuccess(response: EntityArrayResponseType): void { + this.fillComponentAttributesFromResponseHeader(response.headers); + const dataFromBody = this.fillComponentAttributesFromResponseBody(response.body); + this.points = dataFromBody; + } + + protected fillComponentAttributesFromResponseBody(data: IPoints[] | null): IPoints[] { + return data ?? []; + } + + protected fillComponentAttributesFromResponseHeader(headers: HttpHeaders): void { + this.totalItems = Number(headers.get(TOTAL_COUNT_RESPONSE_HEADER)); + } + + protected queryBackend( + page?: number, + predicate?: string, + ascending?: boolean, + currentSearch?: string + ): Observable { + this.isLoading = true; + const pageToLoad: number = page ?? 1; + const queryObject: any = { + page: pageToLoad - 1, + size: this.itemsPerPage, + eagerload: true, + query: currentSearch, + sort: this.getSortQueryParam(predicate, ascending), + }; + if (this.currentSearch && this.currentSearch !== '') { + return this.pointsService.search(queryObject).pipe(tap(() => (this.isLoading = false))); + } else { + return this.pointsService.query(queryObject).pipe(tap(() => (this.isLoading = false))); + } + } + + protected handleNavigation(page = this.page, predicate?: string, ascending?: boolean, currentSearch?: string): void { + const queryParamsObj = { + search: currentSearch, + page, + size: this.itemsPerPage, + sort: this.getSortQueryParam(predicate, ascending), + }; + + this.router.navigate(['./'], { + relativeTo: this.activatedRoute, + queryParams: queryParamsObj, + }); + } + + protected getSortQueryParam(predicate = this.predicate, ascending = this.ascending): string[] { + const ascendingQueryParam = ascending ? ASC : DESC; + if (predicate === '') { + return []; + } else { + return [predicate + ',' + ascendingQueryParam]; + } + } +} diff --git a/src/main/webapp/app/entities/points/points.model.ts b/src/main/webapp/app/entities/points/points.model.ts new file mode 100644 index 00000000..c8e40b0b --- /dev/null +++ b/src/main/webapp/app/entities/points/points.model.ts @@ -0,0 +1,14 @@ +import dayjs from 'dayjs/esm'; +import { IUser } from 'app/entities/user/user.model'; + +export interface IPoints { + id: number; + date?: dayjs.Dayjs | null; + exercise?: number | null; + meals?: number | null; + alcohol?: number | null; + notes?: string | null; + user?: Pick | null; +} + +export type NewPoints = Omit & { id: null }; diff --git a/src/main/webapp/app/entities/points/points.module.ts b/src/main/webapp/app/entities/points/points.module.ts new file mode 100644 index 00000000..2b8bbd3d --- /dev/null +++ b/src/main/webapp/app/entities/points/points.module.ts @@ -0,0 +1,13 @@ +import { NgModule } from '@angular/core'; +import { SharedModule } from 'app/shared/shared.module'; +import { PointsComponent } from './list/points.component'; +import { PointsDetailComponent } from './detail/points-detail.component'; +import { PointsUpdateComponent } from './update/points-update.component'; +import { PointsDeleteDialogComponent } from './delete/points-delete-dialog.component'; +import { PointsRoutingModule } from './route/points-routing.module'; + +@NgModule({ + imports: [SharedModule, PointsRoutingModule], + declarations: [PointsComponent, PointsDetailComponent, PointsUpdateComponent, PointsDeleteDialogComponent], +}) +export class PointsModule {} diff --git a/src/main/webapp/app/entities/points/points.test-samples.ts b/src/main/webapp/app/entities/points/points.test-samples.ts new file mode 100644 index 00000000..25ef69d4 --- /dev/null +++ b/src/main/webapp/app/entities/points/points.test-samples.ts @@ -0,0 +1,34 @@ +import dayjs from 'dayjs/esm'; + +import { IPoints, NewPoints } from './points.model'; + +export const sampleWithRequiredData: IPoints = { + id: 33269, + date: dayjs('2022-11-07'), +}; + +export const sampleWithPartialData: IPoints = { + id: 14659, + date: dayjs('2022-11-07'), + alcohol: 99708, + notes: 'Isle redundant Concrete', +}; + +export const sampleWithFullData: IPoints = { + id: 45763, + date: dayjs('2022-11-07'), + exercise: 94318, + meals: 34931, + alcohol: 5279, + notes: 'invoice drive Borders', +}; + +export const sampleWithNewData: NewPoints = { + date: dayjs('2022-11-07'), + id: null, +}; + +Object.freeze(sampleWithNewData); +Object.freeze(sampleWithRequiredData); +Object.freeze(sampleWithPartialData); +Object.freeze(sampleWithFullData); diff --git a/src/main/webapp/app/entities/points/route/points-routing-resolve.service.spec.ts b/src/main/webapp/app/entities/points/route/points-routing-resolve.service.spec.ts new file mode 100644 index 00000000..15bc90b0 --- /dev/null +++ b/src/main/webapp/app/entities/points/route/points-routing-resolve.service.spec.ts @@ -0,0 +1,89 @@ +import { TestBed } from '@angular/core/testing'; +import { HttpResponse } from '@angular/common/http'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { ActivatedRouteSnapshot, ActivatedRoute, Router, convertToParamMap } from '@angular/router'; +import { RouterTestingModule } from '@angular/router/testing'; +import { of } from 'rxjs'; + +import { IPoints } from '../points.model'; +import { PointsService } from '../service/points.service'; + +import { PointsRoutingResolveService } from './points-routing-resolve.service'; + +describe('Points routing resolve service', () => { + let mockRouter: Router; + let mockActivatedRouteSnapshot: ActivatedRouteSnapshot; + let routingResolveService: PointsRoutingResolveService; + let service: PointsService; + let resultPoints: IPoints | null | undefined; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule, RouterTestingModule.withRoutes([])], + providers: [ + { + provide: ActivatedRoute, + useValue: { + snapshot: { + paramMap: convertToParamMap({}), + }, + }, + }, + ], + }); + mockRouter = TestBed.inject(Router); + jest.spyOn(mockRouter, 'navigate').mockImplementation(() => Promise.resolve(true)); + mockActivatedRouteSnapshot = TestBed.inject(ActivatedRoute).snapshot; + routingResolveService = TestBed.inject(PointsRoutingResolveService); + service = TestBed.inject(PointsService); + resultPoints = undefined; + }); + + describe('resolve', () => { + it('should return IPoints returned by find', () => { + // GIVEN + service.find = jest.fn(id => of(new HttpResponse({ body: { id } }))); + mockActivatedRouteSnapshot.params = { id: 123 }; + + // WHEN + routingResolveService.resolve(mockActivatedRouteSnapshot).subscribe(result => { + resultPoints = result; + }); + + // THEN + expect(service.find).toBeCalledWith(123); + expect(resultPoints).toEqual({ id: 123 }); + }); + + it('should return null if id is not provided', () => { + // GIVEN + service.find = jest.fn(); + mockActivatedRouteSnapshot.params = {}; + + // WHEN + routingResolveService.resolve(mockActivatedRouteSnapshot).subscribe(result => { + resultPoints = result; + }); + + // THEN + expect(service.find).not.toBeCalled(); + expect(resultPoints).toEqual(null); + }); + + it('should route to 404 page if data not found in server', () => { + // GIVEN + jest.spyOn(service, 'find').mockReturnValue(of(new HttpResponse({ body: null }))); + mockActivatedRouteSnapshot.params = { id: 123 }; + + // WHEN + routingResolveService.resolve(mockActivatedRouteSnapshot).subscribe(result => { + resultPoints = result; + }); + + // THEN + expect(service.find).toBeCalledWith(123); + expect(resultPoints).toEqual(undefined); + expect(mockRouter.navigate).toHaveBeenCalledWith(['404']); + }); + }); +}); diff --git a/src/main/webapp/app/entities/points/route/points-routing-resolve.service.ts b/src/main/webapp/app/entities/points/route/points-routing-resolve.service.ts new file mode 100644 index 00000000..e7666642 --- /dev/null +++ b/src/main/webapp/app/entities/points/route/points-routing-resolve.service.ts @@ -0,0 +1,30 @@ +import { Injectable } from '@angular/core'; +import { HttpResponse } from '@angular/common/http'; +import { Resolve, ActivatedRouteSnapshot, Router } from '@angular/router'; +import { Observable, of, EMPTY } from 'rxjs'; +import { mergeMap } from 'rxjs/operators'; + +import { IPoints } from '../points.model'; +import { PointsService } from '../service/points.service'; + +@Injectable({ providedIn: 'root' }) +export class PointsRoutingResolveService implements Resolve { + constructor(protected service: PointsService, protected router: Router) {} + + resolve(route: ActivatedRouteSnapshot): Observable { + const id = route.params['id']; + if (id) { + return this.service.find(id).pipe( + mergeMap((points: HttpResponse) => { + if (points.body) { + return of(points.body); + } else { + this.router.navigate(['404']); + return EMPTY; + } + }) + ); + } + return of(null); + } +} diff --git a/src/main/webapp/app/entities/points/route/points-routing.module.ts b/src/main/webapp/app/entities/points/route/points-routing.module.ts new file mode 100644 index 00000000..9e25fb7a --- /dev/null +++ b/src/main/webapp/app/entities/points/route/points-routing.module.ts @@ -0,0 +1,50 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; + +import { UserRouteAccessService } from 'app/core/auth/user-route-access.service'; +import { PointsComponent } from '../list/points.component'; +import { PointsDetailComponent } from '../detail/points-detail.component'; +import { PointsUpdateComponent } from '../update/points-update.component'; +import { PointsRoutingResolveService } from './points-routing-resolve.service'; +import { ASC } from 'app/config/navigation.constants'; + +const pointsRoute: Routes = [ + { + path: '', + component: PointsComponent, + data: { + defaultSort: 'id,' + ASC, + }, + canActivate: [UserRouteAccessService], + }, + { + path: ':id/view', + component: PointsDetailComponent, + resolve: { + points: PointsRoutingResolveService, + }, + canActivate: [UserRouteAccessService], + }, + { + path: 'new', + component: PointsUpdateComponent, + resolve: { + points: PointsRoutingResolveService, + }, + canActivate: [UserRouteAccessService], + }, + { + path: ':id/edit', + component: PointsUpdateComponent, + resolve: { + points: PointsRoutingResolveService, + }, + canActivate: [UserRouteAccessService], + }, +]; + +@NgModule({ + imports: [RouterModule.forChild(pointsRoute)], + exports: [RouterModule], +}) +export class PointsRoutingModule {} diff --git a/src/main/webapp/app/entities/points/service/points.service.spec.ts b/src/main/webapp/app/entities/points/service/points.service.spec.ts new file mode 100644 index 00000000..20534069 --- /dev/null +++ b/src/main/webapp/app/entities/points/service/points.service.spec.ts @@ -0,0 +1,207 @@ +import { TestBed } from '@angular/core/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; + +import { DATE_FORMAT } from 'app/config/input.constants'; +import { IPoints } from '../points.model'; +import { sampleWithRequiredData, sampleWithNewData, sampleWithPartialData, sampleWithFullData } from '../points.test-samples'; + +import { PointsService, RestPoints } from './points.service'; + +const requireRestSample: RestPoints = { + ...sampleWithRequiredData, + date: sampleWithRequiredData.date?.format(DATE_FORMAT), +}; + +describe('Points Service', () => { + let service: PointsService; + let httpMock: HttpTestingController; + let expectedResult: IPoints | IPoints[] | boolean | null; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + }); + expectedResult = null; + service = TestBed.inject(PointsService); + httpMock = TestBed.inject(HttpTestingController); + }); + + describe('Service methods', () => { + it('should find an element', () => { + const returnedFromService = { ...requireRestSample }; + const expected = { ...sampleWithRequiredData }; + + service.find(123).subscribe(resp => (expectedResult = resp.body)); + + const req = httpMock.expectOne({ method: 'GET' }); + req.flush(returnedFromService); + expect(expectedResult).toMatchObject(expected); + }); + + it('should create a Points', () => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const points = { ...sampleWithNewData }; + const returnedFromService = { ...requireRestSample }; + const expected = { ...sampleWithRequiredData }; + + service.create(points).subscribe(resp => (expectedResult = resp.body)); + + const req = httpMock.expectOne({ method: 'POST' }); + req.flush(returnedFromService); + expect(expectedResult).toMatchObject(expected); + }); + + it('should update a Points', () => { + const points = { ...sampleWithRequiredData }; + const returnedFromService = { ...requireRestSample }; + const expected = { ...sampleWithRequiredData }; + + service.update(points).subscribe(resp => (expectedResult = resp.body)); + + const req = httpMock.expectOne({ method: 'PUT' }); + req.flush(returnedFromService); + expect(expectedResult).toMatchObject(expected); + }); + + it('should partial update a Points', () => { + const patchObject = { ...sampleWithPartialData }; + const returnedFromService = { ...requireRestSample }; + const expected = { ...sampleWithRequiredData }; + + service.partialUpdate(patchObject).subscribe(resp => (expectedResult = resp.body)); + + const req = httpMock.expectOne({ method: 'PATCH' }); + req.flush(returnedFromService); + expect(expectedResult).toMatchObject(expected); + }); + + it('should return a list of Points', () => { + const returnedFromService = { ...requireRestSample }; + + const expected = { ...sampleWithRequiredData }; + + service.query().subscribe(resp => (expectedResult = resp.body)); + + const req = httpMock.expectOne({ method: 'GET' }); + req.flush([returnedFromService]); + httpMock.verify(); + expect(expectedResult).toMatchObject([expected]); + }); + + it('should delete a Points', () => { + const expected = true; + + service.delete(123).subscribe(resp => (expectedResult = resp.ok)); + + const req = httpMock.expectOne({ method: 'DELETE' }); + req.flush({ status: 200 }); + expect(expectedResult).toBe(expected); + }); + + describe('addPointsToCollectionIfMissing', () => { + it('should add a Points to an empty array', () => { + const points: IPoints = sampleWithRequiredData; + expectedResult = service.addPointsToCollectionIfMissing([], points); + expect(expectedResult).toHaveLength(1); + expect(expectedResult).toContain(points); + }); + + it('should not add a Points to an array that contains it', () => { + const points: IPoints = sampleWithRequiredData; + const pointsCollection: IPoints[] = [ + { + ...points, + }, + sampleWithPartialData, + ]; + expectedResult = service.addPointsToCollectionIfMissing(pointsCollection, points); + expect(expectedResult).toHaveLength(2); + }); + + it("should add a Points to an array that doesn't contain it", () => { + const points: IPoints = sampleWithRequiredData; + const pointsCollection: IPoints[] = [sampleWithPartialData]; + expectedResult = service.addPointsToCollectionIfMissing(pointsCollection, points); + expect(expectedResult).toHaveLength(2); + expect(expectedResult).toContain(points); + }); + + it('should add only unique Points to an array', () => { + const pointsArray: IPoints[] = [sampleWithRequiredData, sampleWithPartialData, sampleWithFullData]; + const pointsCollection: IPoints[] = [sampleWithRequiredData]; + expectedResult = service.addPointsToCollectionIfMissing(pointsCollection, ...pointsArray); + expect(expectedResult).toHaveLength(3); + }); + + it('should accept varargs', () => { + const points: IPoints = sampleWithRequiredData; + const points2: IPoints = sampleWithPartialData; + expectedResult = service.addPointsToCollectionIfMissing([], points, points2); + expect(expectedResult).toHaveLength(2); + expect(expectedResult).toContain(points); + expect(expectedResult).toContain(points2); + }); + + it('should accept null and undefined values', () => { + const points: IPoints = sampleWithRequiredData; + expectedResult = service.addPointsToCollectionIfMissing([], null, points, undefined); + expect(expectedResult).toHaveLength(1); + expect(expectedResult).toContain(points); + }); + + it('should return initial array if no Points is added', () => { + const pointsCollection: IPoints[] = [sampleWithRequiredData]; + expectedResult = service.addPointsToCollectionIfMissing(pointsCollection, undefined, null); + expect(expectedResult).toEqual(pointsCollection); + }); + }); + + describe('comparePoints', () => { + it('Should return true if both entities are null', () => { + const entity1 = null; + const entity2 = null; + + const compareResult = service.comparePoints(entity1, entity2); + + expect(compareResult).toEqual(true); + }); + + it('Should return false if one entity is null', () => { + const entity1 = { id: 123 }; + const entity2 = null; + + const compareResult1 = service.comparePoints(entity1, entity2); + const compareResult2 = service.comparePoints(entity2, entity1); + + expect(compareResult1).toEqual(false); + expect(compareResult2).toEqual(false); + }); + + it('Should return false if primaryKey differs', () => { + const entity1 = { id: 123 }; + const entity2 = { id: 456 }; + + const compareResult1 = service.comparePoints(entity1, entity2); + const compareResult2 = service.comparePoints(entity2, entity1); + + expect(compareResult1).toEqual(false); + expect(compareResult2).toEqual(false); + }); + + it('Should return false if primaryKey matches', () => { + const entity1 = { id: 123 }; + const entity2 = { id: 123 }; + + const compareResult1 = service.comparePoints(entity1, entity2); + const compareResult2 = service.comparePoints(entity2, entity1); + + expect(compareResult1).toEqual(true); + expect(compareResult2).toEqual(true); + }); + }); + }); + + afterEach(() => { + httpMock.verify(); + }); +}); diff --git a/src/main/webapp/app/entities/points/service/points.service.ts b/src/main/webapp/app/entities/points/service/points.service.ts new file mode 100644 index 00000000..544279c4 --- /dev/null +++ b/src/main/webapp/app/entities/points/service/points.service.ts @@ -0,0 +1,134 @@ +import { Injectable } from '@angular/core'; +import { HttpClient, HttpResponse } from '@angular/common/http'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; +import dayjs from 'dayjs/esm'; + +import { isPresent } from 'app/core/util/operators'; +import { DATE_FORMAT } from 'app/config/input.constants'; +import { ApplicationConfigService } from 'app/core/config/application-config.service'; +import { createRequestOption } from 'app/core/request/request-util'; +import { SearchWithPagination } from 'app/core/request/request.model'; +import { IPoints, NewPoints } from '../points.model'; + +export type PartialUpdatePoints = Partial & Pick; + +type RestOf = Omit & { + date?: string | null; +}; + +export type RestPoints = RestOf; + +export type NewRestPoints = RestOf; + +export type PartialUpdateRestPoints = RestOf; + +export type EntityResponseType = HttpResponse; +export type EntityArrayResponseType = HttpResponse; + +@Injectable({ providedIn: 'root' }) +export class PointsService { + protected resourceUrl = this.applicationConfigService.getEndpointFor('api/points'); + protected resourceSearchUrl = this.applicationConfigService.getEndpointFor('api/_search/points'); + + constructor(protected http: HttpClient, protected applicationConfigService: ApplicationConfigService) {} + + create(points: NewPoints): Observable { + const copy = this.convertDateFromClient(points); + return this.http + .post(this.resourceUrl, copy, { observe: 'response' }) + .pipe(map(res => this.convertResponseFromServer(res))); + } + + update(points: IPoints): Observable { + const copy = this.convertDateFromClient(points); + return this.http + .put(`${this.resourceUrl}/${this.getPointsIdentifier(points)}`, copy, { observe: 'response' }) + .pipe(map(res => this.convertResponseFromServer(res))); + } + + partialUpdate(points: PartialUpdatePoints): Observable { + const copy = this.convertDateFromClient(points); + return this.http + .patch(`${this.resourceUrl}/${this.getPointsIdentifier(points)}`, copy, { observe: 'response' }) + .pipe(map(res => this.convertResponseFromServer(res))); + } + + find(id: number): Observable { + return this.http + .get(`${this.resourceUrl}/${id}`, { observe: 'response' }) + .pipe(map(res => this.convertResponseFromServer(res))); + } + + query(req?: any): Observable { + const options = createRequestOption(req); + return this.http + .get(this.resourceUrl, { params: options, observe: 'response' }) + .pipe(map(res => this.convertResponseArrayFromServer(res))); + } + + delete(id: number): Observable> { + return this.http.delete(`${this.resourceUrl}/${id}`, { observe: 'response' }); + } + + search(req: SearchWithPagination): Observable { + const options = createRequestOption(req); + return this.http + .get(this.resourceSearchUrl, { params: options, observe: 'response' }) + .pipe(map(res => this.convertResponseArrayFromServer(res))); + } + + getPointsIdentifier(points: Pick): number { + return points.id; + } + + comparePoints(o1: Pick | null, o2: Pick | null): boolean { + return o1 && o2 ? this.getPointsIdentifier(o1) === this.getPointsIdentifier(o2) : o1 === o2; + } + + addPointsToCollectionIfMissing>( + pointsCollection: Type[], + ...pointsToCheck: (Type | null | undefined)[] + ): Type[] { + const points: Type[] = pointsToCheck.filter(isPresent); + if (points.length > 0) { + const pointsCollectionIdentifiers = pointsCollection.map(pointsItem => this.getPointsIdentifier(pointsItem)!); + const pointsToAdd = points.filter(pointsItem => { + const pointsIdentifier = this.getPointsIdentifier(pointsItem); + if (pointsCollectionIdentifiers.includes(pointsIdentifier)) { + return false; + } + pointsCollectionIdentifiers.push(pointsIdentifier); + return true; + }); + return [...pointsToAdd, ...pointsCollection]; + } + return pointsCollection; + } + + protected convertDateFromClient(points: T): RestOf { + return { + ...points, + date: points.date?.format(DATE_FORMAT) ?? null, + }; + } + + protected convertDateFromServer(restPoints: RestPoints): IPoints { + return { + ...restPoints, + date: restPoints.date ? dayjs(restPoints.date) : undefined, + }; + } + + protected convertResponseFromServer(res: HttpResponse): HttpResponse { + return res.clone({ + body: res.body ? this.convertDateFromServer(res.body) : null, + }); + } + + protected convertResponseArrayFromServer(res: HttpResponse): HttpResponse { + return res.clone({ + body: res.body ? res.body.map(item => this.convertDateFromServer(item)) : null, + }); + } +} diff --git a/src/main/webapp/app/entities/points/update/points-form.service.spec.ts b/src/main/webapp/app/entities/points/update/points-form.service.spec.ts new file mode 100644 index 00000000..bea8f2e1 --- /dev/null +++ b/src/main/webapp/app/entities/points/update/points-form.service.spec.ts @@ -0,0 +1,97 @@ +import { TestBed } from '@angular/core/testing'; + +import { sampleWithRequiredData, sampleWithNewData } from '../points.test-samples'; + +import { PointsFormService } from './points-form.service'; + +describe('Points Form Service', () => { + let service: PointsFormService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(PointsFormService); + }); + + describe('Service methods', () => { + describe('createPointsFormGroup', () => { + it('should create a new form with FormControl', () => { + const formGroup = service.createPointsFormGroup(); + + expect(formGroup.controls).toEqual( + expect.objectContaining({ + id: expect.any(Object), + date: expect.any(Object), + exercise: expect.any(Object), + meals: expect.any(Object), + alcohol: expect.any(Object), + notes: expect.any(Object), + user: expect.any(Object), + }) + ); + }); + + it('passing IPoints should create a new form with FormGroup', () => { + const formGroup = service.createPointsFormGroup(sampleWithRequiredData); + + expect(formGroup.controls).toEqual( + expect.objectContaining({ + id: expect.any(Object), + date: expect.any(Object), + exercise: expect.any(Object), + meals: expect.any(Object), + alcohol: expect.any(Object), + notes: expect.any(Object), + user: expect.any(Object), + }) + ); + }); + }); + + describe('getPoints', () => { + it('should return NewPoints for default Points initial value', () => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const formGroup = service.createPointsFormGroup(sampleWithNewData); + + const points = service.getPoints(formGroup) as any; + + expect(points).toMatchObject(sampleWithNewData); + }); + + it('should return NewPoints for empty Points initial value', () => { + const formGroup = service.createPointsFormGroup(); + + const points = service.getPoints(formGroup) as any; + + expect(points).toMatchObject({}); + }); + + it('should return IPoints', () => { + const formGroup = service.createPointsFormGroup(sampleWithRequiredData); + + const points = service.getPoints(formGroup) as any; + + expect(points).toMatchObject(sampleWithRequiredData); + }); + }); + + describe('resetForm', () => { + it('passing IPoints should not enable id FormControl', () => { + const formGroup = service.createPointsFormGroup(); + expect(formGroup.controls.id.disabled).toBe(true); + + service.resetForm(formGroup, sampleWithRequiredData); + + expect(formGroup.controls.id.disabled).toBe(true); + }); + + it('passing NewPoints should disable id FormControl', () => { + const formGroup = service.createPointsFormGroup(sampleWithRequiredData); + expect(formGroup.controls.id.disabled).toBe(true); + + service.resetForm(formGroup, { id: null }); + + expect(formGroup.controls.id.disabled).toBe(true); + }); + }); + }); +}); diff --git a/src/main/webapp/app/entities/points/update/points-form.service.ts b/src/main/webapp/app/entities/points/update/points-form.service.ts new file mode 100644 index 00000000..a7fec14e --- /dev/null +++ b/src/main/webapp/app/entities/points/update/points-form.service.ts @@ -0,0 +1,78 @@ +import { Injectable } from '@angular/core'; +import { FormGroup, FormControl, Validators } from '@angular/forms'; + +import { IPoints, NewPoints } from '../points.model'; + +/** + * A partial Type with required key is used as form input. + */ +type PartialWithRequiredKeyOf = Partial> & { id: T['id'] }; + +/** + * Type for createFormGroup and resetForm argument. + * It accepts IPoints for edit and NewPointsFormGroupInput for create. + */ +type PointsFormGroupInput = IPoints | PartialWithRequiredKeyOf; + +type PointsFormDefaults = Pick; + +type PointsFormGroupContent = { + id: FormControl; + date: FormControl; + exercise: FormControl; + meals: FormControl; + alcohol: FormControl; + notes: FormControl; + user: FormControl; +}; + +export type PointsFormGroup = FormGroup; + +@Injectable({ providedIn: 'root' }) +export class PointsFormService { + createPointsFormGroup(points: PointsFormGroupInput = { id: null }): PointsFormGroup { + const pointsRawValue = { + ...this.getFormDefaults(), + ...points, + }; + return new FormGroup({ + id: new FormControl( + { value: pointsRawValue.id, disabled: true }, + { + nonNullable: true, + validators: [Validators.required], + } + ), + date: new FormControl(pointsRawValue.date, { + validators: [Validators.required], + }), + exercise: new FormControl(pointsRawValue.exercise), + meals: new FormControl(pointsRawValue.meals), + alcohol: new FormControl(pointsRawValue.alcohol), + notes: new FormControl(pointsRawValue.notes, { + validators: [Validators.maxLength(140)], + }), + user: new FormControl(pointsRawValue.user), + }); + } + + getPoints(form: PointsFormGroup): IPoints | NewPoints { + return form.getRawValue() as IPoints | NewPoints; + } + + resetForm(form: PointsFormGroup, points: PointsFormGroupInput): void { + const pointsRawValue = { ...this.getFormDefaults(), ...points }; + form.reset( + { + ...pointsRawValue, + id: { value: pointsRawValue.id, disabled: true }, + } as any /* cast to workaround https://github.com/angular/angular/issues/46458 */ + ); + } + + private getFormDefaults(): PointsFormDefaults { + return { + id: null, + }; + } +} diff --git a/src/main/webapp/app/entities/points/update/points-update.component.html b/src/main/webapp/app/entities/points/update/points-update.component.html new file mode 100644 index 00000000..444cc887 --- /dev/null +++ b/src/main/webapp/app/entities/points/update/points-update.component.html @@ -0,0 +1,94 @@ +
+
+
+

+ Create or edit a Points +

+ +
+ + +
+ + +
+ +
+ +
+ + +
+
+ + This field is required. + +
+
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + This field cannot be longer than 140 characters. + +
+
+ +
+ + +
+
+ +
+ + + +
+
+
+
diff --git a/src/main/webapp/app/entities/points/update/points-update.component.spec.ts b/src/main/webapp/app/entities/points/update/points-update.component.spec.ts new file mode 100644 index 00000000..2e201741 --- /dev/null +++ b/src/main/webapp/app/entities/points/update/points-update.component.spec.ts @@ -0,0 +1,167 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { HttpResponse } from '@angular/common/http'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { FormBuilder } from '@angular/forms'; +import { ActivatedRoute } from '@angular/router'; +import { RouterTestingModule } from '@angular/router/testing'; +import { of, Subject, from } from 'rxjs'; + +import { PointsFormService } from './points-form.service'; +import { PointsService } from '../service/points.service'; +import { IPoints } from '../points.model'; + +import { IUser } from 'app/entities/user/user.model'; +import { UserService } from 'app/entities/user/user.service'; + +import { PointsUpdateComponent } from './points-update.component'; + +describe('Points Management Update Component', () => { + let comp: PointsUpdateComponent; + let fixture: ComponentFixture; + let activatedRoute: ActivatedRoute; + let pointsFormService: PointsFormService; + let pointsService: PointsService; + let userService: UserService; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule, RouterTestingModule.withRoutes([])], + declarations: [PointsUpdateComponent], + providers: [ + FormBuilder, + { + provide: ActivatedRoute, + useValue: { + params: from([{}]), + }, + }, + ], + }) + .overrideTemplate(PointsUpdateComponent, '') + .compileComponents(); + + fixture = TestBed.createComponent(PointsUpdateComponent); + activatedRoute = TestBed.inject(ActivatedRoute); + pointsFormService = TestBed.inject(PointsFormService); + pointsService = TestBed.inject(PointsService); + userService = TestBed.inject(UserService); + + comp = fixture.componentInstance; + }); + + describe('ngOnInit', () => { + it('Should call User query and add missing value', () => { + const points: IPoints = { id: 456 }; + const user: IUser = { id: 30273 }; + points.user = user; + + const userCollection: IUser[] = [{ id: 92271 }]; + jest.spyOn(userService, 'query').mockReturnValue(of(new HttpResponse({ body: userCollection }))); + const additionalUsers = [user]; + const expectedCollection: IUser[] = [...additionalUsers, ...userCollection]; + jest.spyOn(userService, 'addUserToCollectionIfMissing').mockReturnValue(expectedCollection); + + activatedRoute.data = of({ points }); + comp.ngOnInit(); + + expect(userService.query).toHaveBeenCalled(); + expect(userService.addUserToCollectionIfMissing).toHaveBeenCalledWith( + userCollection, + ...additionalUsers.map(expect.objectContaining) + ); + expect(comp.usersSharedCollection).toEqual(expectedCollection); + }); + + it('Should update editForm', () => { + const points: IPoints = { id: 456 }; + const user: IUser = { id: 97538 }; + points.user = user; + + activatedRoute.data = of({ points }); + comp.ngOnInit(); + + expect(comp.usersSharedCollection).toContain(user); + expect(comp.points).toEqual(points); + }); + }); + + describe('save', () => { + it('Should call update service on save for existing entity', () => { + // GIVEN + const saveSubject = new Subject>(); + const points = { id: 123 }; + jest.spyOn(pointsFormService, 'getPoints').mockReturnValue(points); + jest.spyOn(pointsService, 'update').mockReturnValue(saveSubject); + jest.spyOn(comp, 'previousState'); + activatedRoute.data = of({ points }); + comp.ngOnInit(); + + // WHEN + comp.save(); + expect(comp.isSaving).toEqual(true); + saveSubject.next(new HttpResponse({ body: points })); + saveSubject.complete(); + + // THEN + expect(pointsFormService.getPoints).toHaveBeenCalled(); + expect(comp.previousState).toHaveBeenCalled(); + expect(pointsService.update).toHaveBeenCalledWith(expect.objectContaining(points)); + expect(comp.isSaving).toEqual(false); + }); + + it('Should call create service on save for new entity', () => { + // GIVEN + const saveSubject = new Subject>(); + const points = { id: 123 }; + jest.spyOn(pointsFormService, 'getPoints').mockReturnValue({ id: null }); + jest.spyOn(pointsService, 'create').mockReturnValue(saveSubject); + jest.spyOn(comp, 'previousState'); + activatedRoute.data = of({ points: null }); + comp.ngOnInit(); + + // WHEN + comp.save(); + expect(comp.isSaving).toEqual(true); + saveSubject.next(new HttpResponse({ body: points })); + saveSubject.complete(); + + // THEN + expect(pointsFormService.getPoints).toHaveBeenCalled(); + expect(pointsService.create).toHaveBeenCalled(); + expect(comp.isSaving).toEqual(false); + expect(comp.previousState).toHaveBeenCalled(); + }); + + it('Should set isSaving to false on error', () => { + // GIVEN + const saveSubject = new Subject>(); + const points = { id: 123 }; + jest.spyOn(pointsService, 'update').mockReturnValue(saveSubject); + jest.spyOn(comp, 'previousState'); + activatedRoute.data = of({ points }); + comp.ngOnInit(); + + // WHEN + comp.save(); + expect(comp.isSaving).toEqual(true); + saveSubject.error('This is an error!'); + + // THEN + expect(pointsService.update).toHaveBeenCalled(); + expect(comp.isSaving).toEqual(false); + expect(comp.previousState).not.toHaveBeenCalled(); + }); + }); + + describe('Compare relationships', () => { + describe('compareUser', () => { + it('Should forward to userService', () => { + const entity = { id: 123 }; + const entity2 = { id: 456 }; + jest.spyOn(userService, 'compareUser'); + comp.compareUser(entity, entity2); + expect(userService.compareUser).toHaveBeenCalledWith(entity, entity2); + }); + }); + }); +}); diff --git a/src/main/webapp/app/entities/points/update/points-update.component.ts b/src/main/webapp/app/entities/points/update/points-update.component.ts new file mode 100644 index 00000000..405cea87 --- /dev/null +++ b/src/main/webapp/app/entities/points/update/points-update.component.ts @@ -0,0 +1,92 @@ +import { Component, OnInit } from '@angular/core'; +import { HttpResponse } from '@angular/common/http'; +import { ActivatedRoute } from '@angular/router'; +import { Observable } from 'rxjs'; +import { finalize, map } from 'rxjs/operators'; + +import { PointsFormService, PointsFormGroup } from './points-form.service'; +import { IPoints } from '../points.model'; +import { PointsService } from '../service/points.service'; +import { IUser } from 'app/entities/user/user.model'; +import { UserService } from 'app/entities/user/user.service'; + +@Component({ + selector: 'jhi-points-update', + templateUrl: './points-update.component.html', +}) +export class PointsUpdateComponent implements OnInit { + isSaving = false; + points: IPoints | null = null; + + usersSharedCollection: IUser[] = []; + + editForm: PointsFormGroup = this.pointsFormService.createPointsFormGroup(); + + constructor( + protected pointsService: PointsService, + protected pointsFormService: PointsFormService, + protected userService: UserService, + protected activatedRoute: ActivatedRoute + ) {} + + compareUser = (o1: IUser | null, o2: IUser | null): boolean => this.userService.compareUser(o1, o2); + + ngOnInit(): void { + this.activatedRoute.data.subscribe(({ points }) => { + this.points = points; + if (points) { + this.updateForm(points); + } + + this.loadRelationshipsOptions(); + }); + } + + previousState(): void { + window.history.back(); + } + + save(): void { + this.isSaving = true; + const points = this.pointsFormService.getPoints(this.editForm); + if (points.id !== null) { + this.subscribeToSaveResponse(this.pointsService.update(points)); + } else { + this.subscribeToSaveResponse(this.pointsService.create(points)); + } + } + + protected subscribeToSaveResponse(result: Observable>): void { + result.pipe(finalize(() => this.onSaveFinalize())).subscribe({ + next: () => this.onSaveSuccess(), + error: () => this.onSaveError(), + }); + } + + protected onSaveSuccess(): void { + this.previousState(); + } + + protected onSaveError(): void { + // Api for inheritance. + } + + protected onSaveFinalize(): void { + this.isSaving = false; + } + + protected updateForm(points: IPoints): void { + this.points = points; + this.pointsFormService.resetForm(this.editForm, points); + + this.usersSharedCollection = this.userService.addUserToCollectionIfMissing(this.usersSharedCollection, points.user); + } + + protected loadRelationshipsOptions(): void { + this.userService + .query() + .pipe(map((res: HttpResponse) => res.body ?? [])) + .pipe(map((users: IUser[]) => this.userService.addUserToCollectionIfMissing(users, this.points?.user))) + .subscribe((users: IUser[]) => (this.usersSharedCollection = users)); + } +} diff --git a/src/main/webapp/app/entities/preferences/delete/preferences-delete-dialog.component.html b/src/main/webapp/app/entities/preferences/delete/preferences-delete-dialog.component.html new file mode 100644 index 00000000..b62b827e --- /dev/null +++ b/src/main/webapp/app/entities/preferences/delete/preferences-delete-dialog.component.html @@ -0,0 +1,28 @@ +
+ + + + + +
diff --git a/src/main/webapp/app/entities/preferences/delete/preferences-delete-dialog.component.spec.ts b/src/main/webapp/app/entities/preferences/delete/preferences-delete-dialog.component.spec.ts new file mode 100644 index 00000000..bab9a952 --- /dev/null +++ b/src/main/webapp/app/entities/preferences/delete/preferences-delete-dialog.component.spec.ts @@ -0,0 +1,63 @@ +jest.mock('@ng-bootstrap/ng-bootstrap'); + +import { ComponentFixture, TestBed, inject, fakeAsync, tick } from '@angular/core/testing'; +import { HttpResponse } from '@angular/common/http'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { of } from 'rxjs'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; + +import { PreferencesService } from '../service/preferences.service'; + +import { PreferencesDeleteDialogComponent } from './preferences-delete-dialog.component'; + +describe('Preferences Management Delete Component', () => { + let comp: PreferencesDeleteDialogComponent; + let fixture: ComponentFixture; + let service: PreferencesService; + let mockActiveModal: NgbActiveModal; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + declarations: [PreferencesDeleteDialogComponent], + providers: [NgbActiveModal], + }) + .overrideTemplate(PreferencesDeleteDialogComponent, '') + .compileComponents(); + fixture = TestBed.createComponent(PreferencesDeleteDialogComponent); + comp = fixture.componentInstance; + service = TestBed.inject(PreferencesService); + mockActiveModal = TestBed.inject(NgbActiveModal); + }); + + describe('confirmDelete', () => { + it('Should call delete service on confirmDelete', inject( + [], + fakeAsync(() => { + // GIVEN + jest.spyOn(service, 'delete').mockReturnValue(of(new HttpResponse({ body: {} }))); + + // WHEN + comp.confirmDelete(123); + tick(); + + // THEN + expect(service.delete).toHaveBeenCalledWith(123); + expect(mockActiveModal.close).toHaveBeenCalledWith('deleted'); + }) + )); + + it('Should not call delete service on clear', () => { + // GIVEN + jest.spyOn(service, 'delete'); + + // WHEN + comp.cancel(); + + // THEN + expect(service.delete).not.toHaveBeenCalled(); + expect(mockActiveModal.close).not.toHaveBeenCalled(); + expect(mockActiveModal.dismiss).toHaveBeenCalled(); + }); + }); +}); diff --git a/src/main/webapp/app/entities/preferences/delete/preferences-delete-dialog.component.ts b/src/main/webapp/app/entities/preferences/delete/preferences-delete-dialog.component.ts new file mode 100644 index 00000000..edb4324b --- /dev/null +++ b/src/main/webapp/app/entities/preferences/delete/preferences-delete-dialog.component.ts @@ -0,0 +1,25 @@ +import { Component } from '@angular/core'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; + +import { IPreferences } from '../preferences.model'; +import { PreferencesService } from '../service/preferences.service'; +import { ITEM_DELETED_EVENT } from 'app/config/navigation.constants'; + +@Component({ + templateUrl: './preferences-delete-dialog.component.html', +}) +export class PreferencesDeleteDialogComponent { + preferences?: IPreferences; + + constructor(protected preferencesService: PreferencesService, protected activeModal: NgbActiveModal) {} + + cancel(): void { + this.activeModal.dismiss(); + } + + confirmDelete(id: number): void { + this.preferencesService.delete(id).subscribe(() => { + this.activeModal.close(ITEM_DELETED_EVENT); + }); + } +} diff --git a/src/main/webapp/app/entities/preferences/detail/preferences-detail.component.html b/src/main/webapp/app/entities/preferences/detail/preferences-detail.component.html new file mode 100644 index 00000000..dd840787 --- /dev/null +++ b/src/main/webapp/app/entities/preferences/detail/preferences-detail.component.html @@ -0,0 +1,40 @@ +
+
+
+

Preferences

+ +
+ + + + + +
+
ID
+
+ {{ preferences.id }} +
+
Weekly Goal
+
+ {{ preferences.weeklyGoal }} +
+
Weight Units
+
+ {{ preferences.weightUnits }} +
+
User
+
+ {{ preferences.user?.login }} +
+
+ + + + +
+
+
diff --git a/src/main/webapp/app/entities/preferences/detail/preferences-detail.component.spec.ts b/src/main/webapp/app/entities/preferences/detail/preferences-detail.component.spec.ts new file mode 100644 index 00000000..9ccf1ad5 --- /dev/null +++ b/src/main/webapp/app/entities/preferences/detail/preferences-detail.component.spec.ts @@ -0,0 +1,36 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ActivatedRoute } from '@angular/router'; +import { of } from 'rxjs'; + +import { PreferencesDetailComponent } from './preferences-detail.component'; + +describe('Preferences Management Detail Component', () => { + let comp: PreferencesDetailComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [PreferencesDetailComponent], + providers: [ + { + provide: ActivatedRoute, + useValue: { data: of({ preferences: { id: 123 } }) }, + }, + ], + }) + .overrideTemplate(PreferencesDetailComponent, '') + .compileComponents(); + fixture = TestBed.createComponent(PreferencesDetailComponent); + comp = fixture.componentInstance; + }); + + describe('OnInit', () => { + it('Should load preferences on init', () => { + // WHEN + comp.ngOnInit(); + + // THEN + expect(comp.preferences).toEqual(expect.objectContaining({ id: 123 })); + }); + }); +}); diff --git a/src/main/webapp/app/entities/preferences/detail/preferences-detail.component.ts b/src/main/webapp/app/entities/preferences/detail/preferences-detail.component.ts new file mode 100644 index 00000000..a3b783b9 --- /dev/null +++ b/src/main/webapp/app/entities/preferences/detail/preferences-detail.component.ts @@ -0,0 +1,24 @@ +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; + +import { IPreferences } from '../preferences.model'; + +@Component({ + selector: 'jhi-preferences-detail', + templateUrl: './preferences-detail.component.html', +}) +export class PreferencesDetailComponent implements OnInit { + preferences: IPreferences | null = null; + + constructor(protected activatedRoute: ActivatedRoute) {} + + ngOnInit(): void { + this.activatedRoute.data.subscribe(({ preferences }) => { + this.preferences = preferences; + }); + } + + previousState(): void { + window.history.back(); + } +} diff --git a/src/main/webapp/app/entities/preferences/list/preferences.component.html b/src/main/webapp/app/entities/preferences/list/preferences.component.html new file mode 100644 index 00000000..d1e18507 --- /dev/null +++ b/src/main/webapp/app/entities/preferences/list/preferences.component.html @@ -0,0 +1,130 @@ +
+

+ Preferences + +
+ + + +
+

+ + + + + +
+
+
+ + + + + + +
+
+
+ +
+ No Preferences found +
+ +
+ + + + + + + + + + + + + + + + + + + +
+
+ ID + +
+
+
+ Weekly Goal + +
+
+
+ Weight Units + +
+
+
+ User + +
+
+ {{ preferences.id }} + {{ preferences.weeklyGoal }}{{ preferences.weightUnits }} + {{ preferences.user?.login }} + +
+ + + + + +
+
+
+
diff --git a/src/main/webapp/app/entities/preferences/list/preferences.component.spec.ts b/src/main/webapp/app/entities/preferences/list/preferences.component.spec.ts new file mode 100644 index 00000000..6eaee2a9 --- /dev/null +++ b/src/main/webapp/app/entities/preferences/list/preferences.component.spec.ts @@ -0,0 +1,76 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { HttpHeaders, HttpResponse } from '@angular/common/http'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { ActivatedRoute } from '@angular/router'; +import { RouterTestingModule } from '@angular/router/testing'; +import { of } from 'rxjs'; + +import { PreferencesService } from '../service/preferences.service'; + +import { PreferencesComponent } from './preferences.component'; + +describe('Preferences Management Component', () => { + let comp: PreferencesComponent; + let fixture: ComponentFixture; + let service: PreferencesService; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [RouterTestingModule.withRoutes([{ path: 'preferences', component: PreferencesComponent }]), HttpClientTestingModule], + declarations: [PreferencesComponent], + providers: [ + { + provide: ActivatedRoute, + useValue: { + data: of({ + defaultSort: 'id,asc', + }), + queryParamMap: of( + jest.requireActual('@angular/router').convertToParamMap({ + page: '1', + size: '1', + sort: 'id,desc', + }) + ), + snapshot: { queryParams: {} }, + }, + }, + ], + }) + .overrideTemplate(PreferencesComponent, '') + .compileComponents(); + + fixture = TestBed.createComponent(PreferencesComponent); + comp = fixture.componentInstance; + service = TestBed.inject(PreferencesService); + + const headers = new HttpHeaders(); + jest.spyOn(service, 'query').mockReturnValue( + of( + new HttpResponse({ + body: [{ id: 123 }], + headers, + }) + ) + ); + }); + + it('Should call load all on init', () => { + // WHEN + comp.ngOnInit(); + + // THEN + expect(service.query).toHaveBeenCalled(); + expect(comp.preferences?.[0]).toEqual(expect.objectContaining({ id: 123 })); + }); + + describe('trackId', () => { + it('Should forward to preferencesService', () => { + const entity = { id: 123 }; + jest.spyOn(service, 'getPreferencesIdentifier'); + const id = comp.trackId(0, entity); + expect(service.getPreferencesIdentifier).toHaveBeenCalledWith(entity); + expect(id).toBe(entity.id); + }); + }); +}); diff --git a/src/main/webapp/app/entities/preferences/list/preferences.component.ts b/src/main/webapp/app/entities/preferences/list/preferences.component.ts new file mode 100644 index 00000000..9b1fdffb --- /dev/null +++ b/src/main/webapp/app/entities/preferences/list/preferences.component.ts @@ -0,0 +1,143 @@ +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute, Data, ParamMap, Router } from '@angular/router'; +import { combineLatest, filter, Observable, switchMap, tap } from 'rxjs'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; + +import { IPreferences } from '../preferences.model'; +import { ASC, DESC, SORT, ITEM_DELETED_EVENT, DEFAULT_SORT_DATA } from 'app/config/navigation.constants'; +import { EntityArrayResponseType, PreferencesService } from '../service/preferences.service'; +import { PreferencesDeleteDialogComponent } from '../delete/preferences-delete-dialog.component'; +import { SortService } from 'app/shared/sort/sort.service'; + +@Component({ + selector: 'jhi-preferences', + templateUrl: './preferences.component.html', +}) +export class PreferencesComponent implements OnInit { + private static readonly NOT_SORTABLE_FIELDS_AFTER_SEARCH = ['weightUnits']; + + preferences?: IPreferences[]; + isLoading = false; + + predicate = 'id'; + ascending = true; + currentSearch = ''; + + constructor( + protected preferencesService: PreferencesService, + protected activatedRoute: ActivatedRoute, + public router: Router, + protected sortService: SortService, + protected modalService: NgbModal + ) {} + + trackId = (_index: number, item: IPreferences): number => this.preferencesService.getPreferencesIdentifier(item); + + search(query: string): void { + if (query && PreferencesComponent.NOT_SORTABLE_FIELDS_AFTER_SEARCH.includes(this.predicate)) { + this.predicate = 'id'; + this.ascending = true; + } + this.currentSearch = query; + this.navigateToWithComponentValues(); + } + + ngOnInit(): void { + this.load(); + } + + delete(preferences: IPreferences): void { + const modalRef = this.modalService.open(PreferencesDeleteDialogComponent, { size: 'lg', backdrop: 'static' }); + modalRef.componentInstance.preferences = preferences; + // unsubscribe not needed because closed completes on modal close + modalRef.closed + .pipe( + filter(reason => reason === ITEM_DELETED_EVENT), + switchMap(() => this.loadFromBackendWithRouteInformations()) + ) + .subscribe({ + next: (res: EntityArrayResponseType) => { + this.onResponseSuccess(res); + }, + }); + } + + load(): void { + this.loadFromBackendWithRouteInformations().subscribe({ + next: (res: EntityArrayResponseType) => { + this.onResponseSuccess(res); + }, + }); + } + + navigateToWithComponentValues(): void { + this.handleNavigation(this.predicate, this.ascending, this.currentSearch); + } + + protected loadFromBackendWithRouteInformations(): Observable { + return combineLatest([this.activatedRoute.queryParamMap, this.activatedRoute.data]).pipe( + tap(([params, data]) => this.fillComponentAttributeFromRoute(params, data)), + switchMap(() => this.queryBackend(this.predicate, this.ascending, this.currentSearch)) + ); + } + + protected fillComponentAttributeFromRoute(params: ParamMap, data: Data): void { + const sort = (params.get(SORT) ?? data[DEFAULT_SORT_DATA]).split(','); + this.predicate = sort[0]; + this.ascending = sort[1] === ASC; + if (params.has('search') && params.get('search') !== '') { + this.currentSearch = params.get('search') as string; + if (PreferencesComponent.NOT_SORTABLE_FIELDS_AFTER_SEARCH.includes(this.predicate)) { + this.predicate = ''; + } + } + } + + protected onResponseSuccess(response: EntityArrayResponseType): void { + const dataFromBody = this.fillComponentAttributesFromResponseBody(response.body); + this.preferences = this.refineData(dataFromBody); + } + + protected refineData(data: IPreferences[]): IPreferences[] { + return data.sort(this.sortService.startSort(this.predicate, this.ascending ? 1 : -1)); + } + + protected fillComponentAttributesFromResponseBody(data: IPreferences[] | null): IPreferences[] { + return data ?? []; + } + + protected queryBackend(predicate?: string, ascending?: boolean, currentSearch?: string): Observable { + this.isLoading = true; + const queryObject: any = { + eagerload: true, + query: currentSearch, + sort: this.getSortQueryParam(predicate, ascending), + }; + if (this.currentSearch && this.currentSearch !== '') { + return this.preferencesService.search(queryObject).pipe(tap(() => (this.isLoading = false))); + } else { + return this.preferencesService.query(queryObject).pipe(tap(() => (this.isLoading = false))); + } + } + + protected handleNavigation(predicate?: string, ascending?: boolean, currentSearch?: string): void { + const queryParamsObj = { + search: currentSearch, + sort: this.getSortQueryParam(predicate, ascending), + }; + + this.router.navigate(['./'], { + relativeTo: this.activatedRoute, + queryParams: queryParamsObj, + }); + } + + protected getSortQueryParam(predicate = this.predicate, ascending = this.ascending): string[] { + const ascendingQueryParam = ascending ? ASC : DESC; + if (predicate === '') { + return []; + } else { + return [predicate + ',' + ascendingQueryParam]; + } + } +} diff --git a/src/main/webapp/app/entities/preferences/preferences.model.ts b/src/main/webapp/app/entities/preferences/preferences.model.ts new file mode 100644 index 00000000..355f3dfb --- /dev/null +++ b/src/main/webapp/app/entities/preferences/preferences.model.ts @@ -0,0 +1,11 @@ +import { IUser } from 'app/entities/user/user.model'; +import { Units } from 'app/entities/enumerations/units.model'; + +export interface IPreferences { + id: number; + weeklyGoal?: number | null; + weightUnits?: Units | null; + user?: Pick | null; +} + +export type NewPreferences = Omit & { id: null }; diff --git a/src/main/webapp/app/entities/preferences/preferences.module.ts b/src/main/webapp/app/entities/preferences/preferences.module.ts new file mode 100644 index 00000000..1f5bd5ce --- /dev/null +++ b/src/main/webapp/app/entities/preferences/preferences.module.ts @@ -0,0 +1,13 @@ +import { NgModule } from '@angular/core'; +import { SharedModule } from 'app/shared/shared.module'; +import { PreferencesComponent } from './list/preferences.component'; +import { PreferencesDetailComponent } from './detail/preferences-detail.component'; +import { PreferencesUpdateComponent } from './update/preferences-update.component'; +import { PreferencesDeleteDialogComponent } from './delete/preferences-delete-dialog.component'; +import { PreferencesRoutingModule } from './route/preferences-routing.module'; + +@NgModule({ + imports: [SharedModule, PreferencesRoutingModule], + declarations: [PreferencesComponent, PreferencesDetailComponent, PreferencesUpdateComponent, PreferencesDeleteDialogComponent], +}) +export class PreferencesModule {} diff --git a/src/main/webapp/app/entities/preferences/preferences.test-samples.ts b/src/main/webapp/app/entities/preferences/preferences.test-samples.ts new file mode 100644 index 00000000..13e6d642 --- /dev/null +++ b/src/main/webapp/app/entities/preferences/preferences.test-samples.ts @@ -0,0 +1,32 @@ +import { Units } from 'app/entities/enumerations/units.model'; + +import { IPreferences, NewPreferences } from './preferences.model'; + +export const sampleWithRequiredData: IPreferences = { + id: 97604, + weeklyGoal: 13, + weightUnits: Units['LB'], +}; + +export const sampleWithPartialData: IPreferences = { + id: 73829, + weeklyGoal: 12, + weightUnits: Units['KG'], +}; + +export const sampleWithFullData: IPreferences = { + id: 80916, + weeklyGoal: 18, + weightUnits: Units['KG'], +}; + +export const sampleWithNewData: NewPreferences = { + weeklyGoal: 14, + weightUnits: Units['LB'], + id: null, +}; + +Object.freeze(sampleWithNewData); +Object.freeze(sampleWithRequiredData); +Object.freeze(sampleWithPartialData); +Object.freeze(sampleWithFullData); diff --git a/src/main/webapp/app/entities/preferences/route/preferences-routing-resolve.service.spec.ts b/src/main/webapp/app/entities/preferences/route/preferences-routing-resolve.service.spec.ts new file mode 100644 index 00000000..fcb120a8 --- /dev/null +++ b/src/main/webapp/app/entities/preferences/route/preferences-routing-resolve.service.spec.ts @@ -0,0 +1,89 @@ +import { TestBed } from '@angular/core/testing'; +import { HttpResponse } from '@angular/common/http'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { ActivatedRouteSnapshot, ActivatedRoute, Router, convertToParamMap } from '@angular/router'; +import { RouterTestingModule } from '@angular/router/testing'; +import { of } from 'rxjs'; + +import { IPreferences } from '../preferences.model'; +import { PreferencesService } from '../service/preferences.service'; + +import { PreferencesRoutingResolveService } from './preferences-routing-resolve.service'; + +describe('Preferences routing resolve service', () => { + let mockRouter: Router; + let mockActivatedRouteSnapshot: ActivatedRouteSnapshot; + let routingResolveService: PreferencesRoutingResolveService; + let service: PreferencesService; + let resultPreferences: IPreferences | null | undefined; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule, RouterTestingModule.withRoutes([])], + providers: [ + { + provide: ActivatedRoute, + useValue: { + snapshot: { + paramMap: convertToParamMap({}), + }, + }, + }, + ], + }); + mockRouter = TestBed.inject(Router); + jest.spyOn(mockRouter, 'navigate').mockImplementation(() => Promise.resolve(true)); + mockActivatedRouteSnapshot = TestBed.inject(ActivatedRoute).snapshot; + routingResolveService = TestBed.inject(PreferencesRoutingResolveService); + service = TestBed.inject(PreferencesService); + resultPreferences = undefined; + }); + + describe('resolve', () => { + it('should return IPreferences returned by find', () => { + // GIVEN + service.find = jest.fn(id => of(new HttpResponse({ body: { id } }))); + mockActivatedRouteSnapshot.params = { id: 123 }; + + // WHEN + routingResolveService.resolve(mockActivatedRouteSnapshot).subscribe(result => { + resultPreferences = result; + }); + + // THEN + expect(service.find).toBeCalledWith(123); + expect(resultPreferences).toEqual({ id: 123 }); + }); + + it('should return null if id is not provided', () => { + // GIVEN + service.find = jest.fn(); + mockActivatedRouteSnapshot.params = {}; + + // WHEN + routingResolveService.resolve(mockActivatedRouteSnapshot).subscribe(result => { + resultPreferences = result; + }); + + // THEN + expect(service.find).not.toBeCalled(); + expect(resultPreferences).toEqual(null); + }); + + it('should route to 404 page if data not found in server', () => { + // GIVEN + jest.spyOn(service, 'find').mockReturnValue(of(new HttpResponse({ body: null }))); + mockActivatedRouteSnapshot.params = { id: 123 }; + + // WHEN + routingResolveService.resolve(mockActivatedRouteSnapshot).subscribe(result => { + resultPreferences = result; + }); + + // THEN + expect(service.find).toBeCalledWith(123); + expect(resultPreferences).toEqual(undefined); + expect(mockRouter.navigate).toHaveBeenCalledWith(['404']); + }); + }); +}); diff --git a/src/main/webapp/app/entities/preferences/route/preferences-routing-resolve.service.ts b/src/main/webapp/app/entities/preferences/route/preferences-routing-resolve.service.ts new file mode 100644 index 00000000..1e30aa6c --- /dev/null +++ b/src/main/webapp/app/entities/preferences/route/preferences-routing-resolve.service.ts @@ -0,0 +1,30 @@ +import { Injectable } from '@angular/core'; +import { HttpResponse } from '@angular/common/http'; +import { Resolve, ActivatedRouteSnapshot, Router } from '@angular/router'; +import { Observable, of, EMPTY } from 'rxjs'; +import { mergeMap } from 'rxjs/operators'; + +import { IPreferences } from '../preferences.model'; +import { PreferencesService } from '../service/preferences.service'; + +@Injectable({ providedIn: 'root' }) +export class PreferencesRoutingResolveService implements Resolve { + constructor(protected service: PreferencesService, protected router: Router) {} + + resolve(route: ActivatedRouteSnapshot): Observable { + const id = route.params['id']; + if (id) { + return this.service.find(id).pipe( + mergeMap((preferences: HttpResponse) => { + if (preferences.body) { + return of(preferences.body); + } else { + this.router.navigate(['404']); + return EMPTY; + } + }) + ); + } + return of(null); + } +} diff --git a/src/main/webapp/app/entities/preferences/route/preferences-routing.module.ts b/src/main/webapp/app/entities/preferences/route/preferences-routing.module.ts new file mode 100644 index 00000000..b5caf4e5 --- /dev/null +++ b/src/main/webapp/app/entities/preferences/route/preferences-routing.module.ts @@ -0,0 +1,50 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; + +import { UserRouteAccessService } from 'app/core/auth/user-route-access.service'; +import { PreferencesComponent } from '../list/preferences.component'; +import { PreferencesDetailComponent } from '../detail/preferences-detail.component'; +import { PreferencesUpdateComponent } from '../update/preferences-update.component'; +import { PreferencesRoutingResolveService } from './preferences-routing-resolve.service'; +import { ASC } from 'app/config/navigation.constants'; + +const preferencesRoute: Routes = [ + { + path: '', + component: PreferencesComponent, + data: { + defaultSort: 'id,' + ASC, + }, + canActivate: [UserRouteAccessService], + }, + { + path: ':id/view', + component: PreferencesDetailComponent, + resolve: { + preferences: PreferencesRoutingResolveService, + }, + canActivate: [UserRouteAccessService], + }, + { + path: 'new', + component: PreferencesUpdateComponent, + resolve: { + preferences: PreferencesRoutingResolveService, + }, + canActivate: [UserRouteAccessService], + }, + { + path: ':id/edit', + component: PreferencesUpdateComponent, + resolve: { + preferences: PreferencesRoutingResolveService, + }, + canActivate: [UserRouteAccessService], + }, +]; + +@NgModule({ + imports: [RouterModule.forChild(preferencesRoute)], + exports: [RouterModule], +}) +export class PreferencesRoutingModule {} diff --git a/src/main/webapp/app/entities/preferences/service/preferences.service.spec.ts b/src/main/webapp/app/entities/preferences/service/preferences.service.spec.ts new file mode 100644 index 00000000..9e821f30 --- /dev/null +++ b/src/main/webapp/app/entities/preferences/service/preferences.service.spec.ts @@ -0,0 +1,205 @@ +import { TestBed } from '@angular/core/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; + +import { IPreferences } from '../preferences.model'; +import { sampleWithRequiredData, sampleWithNewData, sampleWithPartialData, sampleWithFullData } from '../preferences.test-samples'; + +import { PreferencesService } from './preferences.service'; + +const requireRestSample: IPreferences = { + ...sampleWithRequiredData, +}; + +describe('Preferences Service', () => { + let service: PreferencesService; + let httpMock: HttpTestingController; + let expectedResult: IPreferences | IPreferences[] | boolean | null; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + }); + expectedResult = null; + service = TestBed.inject(PreferencesService); + httpMock = TestBed.inject(HttpTestingController); + }); + + describe('Service methods', () => { + it('should find an element', () => { + const returnedFromService = { ...requireRestSample }; + const expected = { ...sampleWithRequiredData }; + + service.find(123).subscribe(resp => (expectedResult = resp.body)); + + const req = httpMock.expectOne({ method: 'GET' }); + req.flush(returnedFromService); + expect(expectedResult).toMatchObject(expected); + }); + + it('should create a Preferences', () => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const preferences = { ...sampleWithNewData }; + const returnedFromService = { ...requireRestSample }; + const expected = { ...sampleWithRequiredData }; + + service.create(preferences).subscribe(resp => (expectedResult = resp.body)); + + const req = httpMock.expectOne({ method: 'POST' }); + req.flush(returnedFromService); + expect(expectedResult).toMatchObject(expected); + }); + + it('should update a Preferences', () => { + const preferences = { ...sampleWithRequiredData }; + const returnedFromService = { ...requireRestSample }; + const expected = { ...sampleWithRequiredData }; + + service.update(preferences).subscribe(resp => (expectedResult = resp.body)); + + const req = httpMock.expectOne({ method: 'PUT' }); + req.flush(returnedFromService); + expect(expectedResult).toMatchObject(expected); + }); + + it('should partial update a Preferences', () => { + const patchObject = { ...sampleWithPartialData }; + const returnedFromService = { ...requireRestSample }; + const expected = { ...sampleWithRequiredData }; + + service.partialUpdate(patchObject).subscribe(resp => (expectedResult = resp.body)); + + const req = httpMock.expectOne({ method: 'PATCH' }); + req.flush(returnedFromService); + expect(expectedResult).toMatchObject(expected); + }); + + it('should return a list of Preferences', () => { + const returnedFromService = { ...requireRestSample }; + + const expected = { ...sampleWithRequiredData }; + + service.query().subscribe(resp => (expectedResult = resp.body)); + + const req = httpMock.expectOne({ method: 'GET' }); + req.flush([returnedFromService]); + httpMock.verify(); + expect(expectedResult).toMatchObject([expected]); + }); + + it('should delete a Preferences', () => { + const expected = true; + + service.delete(123).subscribe(resp => (expectedResult = resp.ok)); + + const req = httpMock.expectOne({ method: 'DELETE' }); + req.flush({ status: 200 }); + expect(expectedResult).toBe(expected); + }); + + describe('addPreferencesToCollectionIfMissing', () => { + it('should add a Preferences to an empty array', () => { + const preferences: IPreferences = sampleWithRequiredData; + expectedResult = service.addPreferencesToCollectionIfMissing([], preferences); + expect(expectedResult).toHaveLength(1); + expect(expectedResult).toContain(preferences); + }); + + it('should not add a Preferences to an array that contains it', () => { + const preferences: IPreferences = sampleWithRequiredData; + const preferencesCollection: IPreferences[] = [ + { + ...preferences, + }, + sampleWithPartialData, + ]; + expectedResult = service.addPreferencesToCollectionIfMissing(preferencesCollection, preferences); + expect(expectedResult).toHaveLength(2); + }); + + it("should add a Preferences to an array that doesn't contain it", () => { + const preferences: IPreferences = sampleWithRequiredData; + const preferencesCollection: IPreferences[] = [sampleWithPartialData]; + expectedResult = service.addPreferencesToCollectionIfMissing(preferencesCollection, preferences); + expect(expectedResult).toHaveLength(2); + expect(expectedResult).toContain(preferences); + }); + + it('should add only unique Preferences to an array', () => { + const preferencesArray: IPreferences[] = [sampleWithRequiredData, sampleWithPartialData, sampleWithFullData]; + const preferencesCollection: IPreferences[] = [sampleWithRequiredData]; + expectedResult = service.addPreferencesToCollectionIfMissing(preferencesCollection, ...preferencesArray); + expect(expectedResult).toHaveLength(3); + }); + + it('should accept varargs', () => { + const preferences: IPreferences = sampleWithRequiredData; + const preferences2: IPreferences = sampleWithPartialData; + expectedResult = service.addPreferencesToCollectionIfMissing([], preferences, preferences2); + expect(expectedResult).toHaveLength(2); + expect(expectedResult).toContain(preferences); + expect(expectedResult).toContain(preferences2); + }); + + it('should accept null and undefined values', () => { + const preferences: IPreferences = sampleWithRequiredData; + expectedResult = service.addPreferencesToCollectionIfMissing([], null, preferences, undefined); + expect(expectedResult).toHaveLength(1); + expect(expectedResult).toContain(preferences); + }); + + it('should return initial array if no Preferences is added', () => { + const preferencesCollection: IPreferences[] = [sampleWithRequiredData]; + expectedResult = service.addPreferencesToCollectionIfMissing(preferencesCollection, undefined, null); + expect(expectedResult).toEqual(preferencesCollection); + }); + }); + + describe('comparePreferences', () => { + it('Should return true if both entities are null', () => { + const entity1 = null; + const entity2 = null; + + const compareResult = service.comparePreferences(entity1, entity2); + + expect(compareResult).toEqual(true); + }); + + it('Should return false if one entity is null', () => { + const entity1 = { id: 123 }; + const entity2 = null; + + const compareResult1 = service.comparePreferences(entity1, entity2); + const compareResult2 = service.comparePreferences(entity2, entity1); + + expect(compareResult1).toEqual(false); + expect(compareResult2).toEqual(false); + }); + + it('Should return false if primaryKey differs', () => { + const entity1 = { id: 123 }; + const entity2 = { id: 456 }; + + const compareResult1 = service.comparePreferences(entity1, entity2); + const compareResult2 = service.comparePreferences(entity2, entity1); + + expect(compareResult1).toEqual(false); + expect(compareResult2).toEqual(false); + }); + + it('Should return false if primaryKey matches', () => { + const entity1 = { id: 123 }; + const entity2 = { id: 123 }; + + const compareResult1 = service.comparePreferences(entity1, entity2); + const compareResult2 = service.comparePreferences(entity2, entity1); + + expect(compareResult1).toEqual(true); + expect(compareResult2).toEqual(true); + }); + }); + }); + + afterEach(() => { + httpMock.verify(); + }); +}); diff --git a/src/main/webapp/app/entities/preferences/service/preferences.service.ts b/src/main/webapp/app/entities/preferences/service/preferences.service.ts new file mode 100644 index 00000000..06623941 --- /dev/null +++ b/src/main/webapp/app/entities/preferences/service/preferences.service.ts @@ -0,0 +1,86 @@ +import { Injectable } from '@angular/core'; +import { HttpClient, HttpResponse } from '@angular/common/http'; +import { Observable } from 'rxjs'; + +import { isPresent } from 'app/core/util/operators'; +import { ApplicationConfigService } from 'app/core/config/application-config.service'; +import { createRequestOption } from 'app/core/request/request-util'; +import { Search } from 'app/core/request/request.model'; +import { IPreferences, NewPreferences } from '../preferences.model'; + +export type PartialUpdatePreferences = Partial & Pick; + +export type EntityResponseType = HttpResponse; +export type EntityArrayResponseType = HttpResponse; + +@Injectable({ providedIn: 'root' }) +export class PreferencesService { + protected resourceUrl = this.applicationConfigService.getEndpointFor('api/preferences'); + protected resourceSearchUrl = this.applicationConfigService.getEndpointFor('api/_search/preferences'); + + constructor(protected http: HttpClient, protected applicationConfigService: ApplicationConfigService) {} + + create(preferences: NewPreferences): Observable { + return this.http.post(this.resourceUrl, preferences, { observe: 'response' }); + } + + update(preferences: IPreferences): Observable { + return this.http.put(`${this.resourceUrl}/${this.getPreferencesIdentifier(preferences)}`, preferences, { + observe: 'response', + }); + } + + partialUpdate(preferences: PartialUpdatePreferences): Observable { + return this.http.patch(`${this.resourceUrl}/${this.getPreferencesIdentifier(preferences)}`, preferences, { + observe: 'response', + }); + } + + find(id: number): Observable { + return this.http.get(`${this.resourceUrl}/${id}`, { observe: 'response' }); + } + + query(req?: any): Observable { + const options = createRequestOption(req); + return this.http.get(this.resourceUrl, { params: options, observe: 'response' }); + } + + delete(id: number): Observable> { + return this.http.delete(`${this.resourceUrl}/${id}`, { observe: 'response' }); + } + + search(req: Search): Observable { + const options = createRequestOption(req); + return this.http.get(this.resourceSearchUrl, { params: options, observe: 'response' }); + } + + getPreferencesIdentifier(preferences: Pick): number { + return preferences.id; + } + + comparePreferences(o1: Pick | null, o2: Pick | null): boolean { + return o1 && o2 ? this.getPreferencesIdentifier(o1) === this.getPreferencesIdentifier(o2) : o1 === o2; + } + + addPreferencesToCollectionIfMissing>( + preferencesCollection: Type[], + ...preferencesToCheck: (Type | null | undefined)[] + ): Type[] { + const preferences: Type[] = preferencesToCheck.filter(isPresent); + if (preferences.length > 0) { + const preferencesCollectionIdentifiers = preferencesCollection.map( + preferencesItem => this.getPreferencesIdentifier(preferencesItem)! + ); + const preferencesToAdd = preferences.filter(preferencesItem => { + const preferencesIdentifier = this.getPreferencesIdentifier(preferencesItem); + if (preferencesCollectionIdentifiers.includes(preferencesIdentifier)) { + return false; + } + preferencesCollectionIdentifiers.push(preferencesIdentifier); + return true; + }); + return [...preferencesToAdd, ...preferencesCollection]; + } + return preferencesCollection; + } +} diff --git a/src/main/webapp/app/entities/preferences/update/preferences-form.service.spec.ts b/src/main/webapp/app/entities/preferences/update/preferences-form.service.spec.ts new file mode 100644 index 00000000..0b68481a --- /dev/null +++ b/src/main/webapp/app/entities/preferences/update/preferences-form.service.spec.ts @@ -0,0 +1,91 @@ +import { TestBed } from '@angular/core/testing'; + +import { sampleWithRequiredData, sampleWithNewData } from '../preferences.test-samples'; + +import { PreferencesFormService } from './preferences-form.service'; + +describe('Preferences Form Service', () => { + let service: PreferencesFormService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(PreferencesFormService); + }); + + describe('Service methods', () => { + describe('createPreferencesFormGroup', () => { + it('should create a new form with FormControl', () => { + const formGroup = service.createPreferencesFormGroup(); + + expect(formGroup.controls).toEqual( + expect.objectContaining({ + id: expect.any(Object), + weeklyGoal: expect.any(Object), + weightUnits: expect.any(Object), + user: expect.any(Object), + }) + ); + }); + + it('passing IPreferences should create a new form with FormGroup', () => { + const formGroup = service.createPreferencesFormGroup(sampleWithRequiredData); + + expect(formGroup.controls).toEqual( + expect.objectContaining({ + id: expect.any(Object), + weeklyGoal: expect.any(Object), + weightUnits: expect.any(Object), + user: expect.any(Object), + }) + ); + }); + }); + + describe('getPreferences', () => { + it('should return NewPreferences for default Preferences initial value', () => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const formGroup = service.createPreferencesFormGroup(sampleWithNewData); + + const preferences = service.getPreferences(formGroup) as any; + + expect(preferences).toMatchObject(sampleWithNewData); + }); + + it('should return NewPreferences for empty Preferences initial value', () => { + const formGroup = service.createPreferencesFormGroup(); + + const preferences = service.getPreferences(formGroup) as any; + + expect(preferences).toMatchObject({}); + }); + + it('should return IPreferences', () => { + const formGroup = service.createPreferencesFormGroup(sampleWithRequiredData); + + const preferences = service.getPreferences(formGroup) as any; + + expect(preferences).toMatchObject(sampleWithRequiredData); + }); + }); + + describe('resetForm', () => { + it('passing IPreferences should not enable id FormControl', () => { + const formGroup = service.createPreferencesFormGroup(); + expect(formGroup.controls.id.disabled).toBe(true); + + service.resetForm(formGroup, sampleWithRequiredData); + + expect(formGroup.controls.id.disabled).toBe(true); + }); + + it('passing NewPreferences should disable id FormControl', () => { + const formGroup = service.createPreferencesFormGroup(sampleWithRequiredData); + expect(formGroup.controls.id.disabled).toBe(true); + + service.resetForm(formGroup, { id: null }); + + expect(formGroup.controls.id.disabled).toBe(true); + }); + }); + }); +}); diff --git a/src/main/webapp/app/entities/preferences/update/preferences-form.service.ts b/src/main/webapp/app/entities/preferences/update/preferences-form.service.ts new file mode 100644 index 00000000..d349b993 --- /dev/null +++ b/src/main/webapp/app/entities/preferences/update/preferences-form.service.ts @@ -0,0 +1,72 @@ +import { Injectable } from '@angular/core'; +import { FormGroup, FormControl, Validators } from '@angular/forms'; + +import { IPreferences, NewPreferences } from '../preferences.model'; + +/** + * A partial Type with required key is used as form input. + */ +type PartialWithRequiredKeyOf = Partial> & { id: T['id'] }; + +/** + * Type for createFormGroup and resetForm argument. + * It accepts IPreferences for edit and NewPreferencesFormGroupInput for create. + */ +type PreferencesFormGroupInput = IPreferences | PartialWithRequiredKeyOf; + +type PreferencesFormDefaults = Pick; + +type PreferencesFormGroupContent = { + id: FormControl; + weeklyGoal: FormControl; + weightUnits: FormControl; + user: FormControl; +}; + +export type PreferencesFormGroup = FormGroup; + +@Injectable({ providedIn: 'root' }) +export class PreferencesFormService { + createPreferencesFormGroup(preferences: PreferencesFormGroupInput = { id: null }): PreferencesFormGroup { + const preferencesRawValue = { + ...this.getFormDefaults(), + ...preferences, + }; + return new FormGroup({ + id: new FormControl( + { value: preferencesRawValue.id, disabled: true }, + { + nonNullable: true, + validators: [Validators.required], + } + ), + weeklyGoal: new FormControl(preferencesRawValue.weeklyGoal, { + validators: [Validators.required, Validators.min(10), Validators.max(21)], + }), + weightUnits: new FormControl(preferencesRawValue.weightUnits, { + validators: [Validators.required], + }), + user: new FormControl(preferencesRawValue.user), + }); + } + + getPreferences(form: PreferencesFormGroup): IPreferences | NewPreferences { + return form.getRawValue() as IPreferences | NewPreferences; + } + + resetForm(form: PreferencesFormGroup, preferences: PreferencesFormGroupInput): void { + const preferencesRawValue = { ...this.getFormDefaults(), ...preferences }; + form.reset( + { + ...preferencesRawValue, + id: { value: preferencesRawValue.id, disabled: true }, + } as any /* cast to workaround https://github.com/angular/angular/issues/46458 */ + ); + } + + private getFormDefaults(): PreferencesFormDefaults { + return { + id: null, + }; + } +} diff --git a/src/main/webapp/app/entities/preferences/update/preferences-update.component.html b/src/main/webapp/app/entities/preferences/update/preferences-update.component.html new file mode 100644 index 00000000..e44e160e --- /dev/null +++ b/src/main/webapp/app/entities/preferences/update/preferences-update.component.html @@ -0,0 +1,107 @@ +
+
+
+

+ Create or edit a Preferences +

+ +
+ + +
+ + +
+ +
+ + +
+ + This field is required. + + + This field should be at least 10. + + + This field cannot be more than 21. + + + This field should be a number. + +
+
+ +
+ + +
+ + This field is required. + +
+
+ +
+ + +
+
+ +
+ + + +
+
+
+
diff --git a/src/main/webapp/app/entities/preferences/update/preferences-update.component.spec.ts b/src/main/webapp/app/entities/preferences/update/preferences-update.component.spec.ts new file mode 100644 index 00000000..67981c62 --- /dev/null +++ b/src/main/webapp/app/entities/preferences/update/preferences-update.component.spec.ts @@ -0,0 +1,167 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { HttpResponse } from '@angular/common/http'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { FormBuilder } from '@angular/forms'; +import { ActivatedRoute } from '@angular/router'; +import { RouterTestingModule } from '@angular/router/testing'; +import { of, Subject, from } from 'rxjs'; + +import { PreferencesFormService } from './preferences-form.service'; +import { PreferencesService } from '../service/preferences.service'; +import { IPreferences } from '../preferences.model'; + +import { IUser } from 'app/entities/user/user.model'; +import { UserService } from 'app/entities/user/user.service'; + +import { PreferencesUpdateComponent } from './preferences-update.component'; + +describe('Preferences Management Update Component', () => { + let comp: PreferencesUpdateComponent; + let fixture: ComponentFixture; + let activatedRoute: ActivatedRoute; + let preferencesFormService: PreferencesFormService; + let preferencesService: PreferencesService; + let userService: UserService; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule, RouterTestingModule.withRoutes([])], + declarations: [PreferencesUpdateComponent], + providers: [ + FormBuilder, + { + provide: ActivatedRoute, + useValue: { + params: from([{}]), + }, + }, + ], + }) + .overrideTemplate(PreferencesUpdateComponent, '') + .compileComponents(); + + fixture = TestBed.createComponent(PreferencesUpdateComponent); + activatedRoute = TestBed.inject(ActivatedRoute); + preferencesFormService = TestBed.inject(PreferencesFormService); + preferencesService = TestBed.inject(PreferencesService); + userService = TestBed.inject(UserService); + + comp = fixture.componentInstance; + }); + + describe('ngOnInit', () => { + it('Should call User query and add missing value', () => { + const preferences: IPreferences = { id: 456 }; + const user: IUser = { id: 76797 }; + preferences.user = user; + + const userCollection: IUser[] = [{ id: 99188 }]; + jest.spyOn(userService, 'query').mockReturnValue(of(new HttpResponse({ body: userCollection }))); + const additionalUsers = [user]; + const expectedCollection: IUser[] = [...additionalUsers, ...userCollection]; + jest.spyOn(userService, 'addUserToCollectionIfMissing').mockReturnValue(expectedCollection); + + activatedRoute.data = of({ preferences }); + comp.ngOnInit(); + + expect(userService.query).toHaveBeenCalled(); + expect(userService.addUserToCollectionIfMissing).toHaveBeenCalledWith( + userCollection, + ...additionalUsers.map(expect.objectContaining) + ); + expect(comp.usersSharedCollection).toEqual(expectedCollection); + }); + + it('Should update editForm', () => { + const preferences: IPreferences = { id: 456 }; + const user: IUser = { id: 79175 }; + preferences.user = user; + + activatedRoute.data = of({ preferences }); + comp.ngOnInit(); + + expect(comp.usersSharedCollection).toContain(user); + expect(comp.preferences).toEqual(preferences); + }); + }); + + describe('save', () => { + it('Should call update service on save for existing entity', () => { + // GIVEN + const saveSubject = new Subject>(); + const preferences = { id: 123 }; + jest.spyOn(preferencesFormService, 'getPreferences').mockReturnValue(preferences); + jest.spyOn(preferencesService, 'update').mockReturnValue(saveSubject); + jest.spyOn(comp, 'previousState'); + activatedRoute.data = of({ preferences }); + comp.ngOnInit(); + + // WHEN + comp.save(); + expect(comp.isSaving).toEqual(true); + saveSubject.next(new HttpResponse({ body: preferences })); + saveSubject.complete(); + + // THEN + expect(preferencesFormService.getPreferences).toHaveBeenCalled(); + expect(comp.previousState).toHaveBeenCalled(); + expect(preferencesService.update).toHaveBeenCalledWith(expect.objectContaining(preferences)); + expect(comp.isSaving).toEqual(false); + }); + + it('Should call create service on save for new entity', () => { + // GIVEN + const saveSubject = new Subject>(); + const preferences = { id: 123 }; + jest.spyOn(preferencesFormService, 'getPreferences').mockReturnValue({ id: null }); + jest.spyOn(preferencesService, 'create').mockReturnValue(saveSubject); + jest.spyOn(comp, 'previousState'); + activatedRoute.data = of({ preferences: null }); + comp.ngOnInit(); + + // WHEN + comp.save(); + expect(comp.isSaving).toEqual(true); + saveSubject.next(new HttpResponse({ body: preferences })); + saveSubject.complete(); + + // THEN + expect(preferencesFormService.getPreferences).toHaveBeenCalled(); + expect(preferencesService.create).toHaveBeenCalled(); + expect(comp.isSaving).toEqual(false); + expect(comp.previousState).toHaveBeenCalled(); + }); + + it('Should set isSaving to false on error', () => { + // GIVEN + const saveSubject = new Subject>(); + const preferences = { id: 123 }; + jest.spyOn(preferencesService, 'update').mockReturnValue(saveSubject); + jest.spyOn(comp, 'previousState'); + activatedRoute.data = of({ preferences }); + comp.ngOnInit(); + + // WHEN + comp.save(); + expect(comp.isSaving).toEqual(true); + saveSubject.error('This is an error!'); + + // THEN + expect(preferencesService.update).toHaveBeenCalled(); + expect(comp.isSaving).toEqual(false); + expect(comp.previousState).not.toHaveBeenCalled(); + }); + }); + + describe('Compare relationships', () => { + describe('compareUser', () => { + it('Should forward to userService', () => { + const entity = { id: 123 }; + const entity2 = { id: 456 }; + jest.spyOn(userService, 'compareUser'); + comp.compareUser(entity, entity2); + expect(userService.compareUser).toHaveBeenCalledWith(entity, entity2); + }); + }); + }); +}); diff --git a/src/main/webapp/app/entities/preferences/update/preferences-update.component.ts b/src/main/webapp/app/entities/preferences/update/preferences-update.component.ts new file mode 100644 index 00000000..17b0f45f --- /dev/null +++ b/src/main/webapp/app/entities/preferences/update/preferences-update.component.ts @@ -0,0 +1,94 @@ +import { Component, OnInit } from '@angular/core'; +import { HttpResponse } from '@angular/common/http'; +import { ActivatedRoute } from '@angular/router'; +import { Observable } from 'rxjs'; +import { finalize, map } from 'rxjs/operators'; + +import { PreferencesFormService, PreferencesFormGroup } from './preferences-form.service'; +import { IPreferences } from '../preferences.model'; +import { PreferencesService } from '../service/preferences.service'; +import { IUser } from 'app/entities/user/user.model'; +import { UserService } from 'app/entities/user/user.service'; +import { Units } from 'app/entities/enumerations/units.model'; + +@Component({ + selector: 'jhi-preferences-update', + templateUrl: './preferences-update.component.html', +}) +export class PreferencesUpdateComponent implements OnInit { + isSaving = false; + preferences: IPreferences | null = null; + unitsValues = Object.keys(Units); + + usersSharedCollection: IUser[] = []; + + editForm: PreferencesFormGroup = this.preferencesFormService.createPreferencesFormGroup(); + + constructor( + protected preferencesService: PreferencesService, + protected preferencesFormService: PreferencesFormService, + protected userService: UserService, + protected activatedRoute: ActivatedRoute + ) {} + + compareUser = (o1: IUser | null, o2: IUser | null): boolean => this.userService.compareUser(o1, o2); + + ngOnInit(): void { + this.activatedRoute.data.subscribe(({ preferences }) => { + this.preferences = preferences; + if (preferences) { + this.updateForm(preferences); + } + + this.loadRelationshipsOptions(); + }); + } + + previousState(): void { + window.history.back(); + } + + save(): void { + this.isSaving = true; + const preferences = this.preferencesFormService.getPreferences(this.editForm); + if (preferences.id !== null) { + this.subscribeToSaveResponse(this.preferencesService.update(preferences)); + } else { + this.subscribeToSaveResponse(this.preferencesService.create(preferences)); + } + } + + protected subscribeToSaveResponse(result: Observable>): void { + result.pipe(finalize(() => this.onSaveFinalize())).subscribe({ + next: () => this.onSaveSuccess(), + error: () => this.onSaveError(), + }); + } + + protected onSaveSuccess(): void { + this.previousState(); + } + + protected onSaveError(): void { + // Api for inheritance. + } + + protected onSaveFinalize(): void { + this.isSaving = false; + } + + protected updateForm(preferences: IPreferences): void { + this.preferences = preferences; + this.preferencesFormService.resetForm(this.editForm, preferences); + + this.usersSharedCollection = this.userService.addUserToCollectionIfMissing(this.usersSharedCollection, preferences.user); + } + + protected loadRelationshipsOptions(): void { + this.userService + .query() + .pipe(map((res: HttpResponse) => res.body ?? [])) + .pipe(map((users: IUser[]) => this.userService.addUserToCollectionIfMissing(users, this.preferences?.user))) + .subscribe((users: IUser[]) => (this.usersSharedCollection = users)); + } +} diff --git a/src/main/webapp/app/entities/user/user.model.ts b/src/main/webapp/app/entities/user/user.model.ts new file mode 100644 index 00000000..202428eb --- /dev/null +++ b/src/main/webapp/app/entities/user/user.model.ts @@ -0,0 +1,12 @@ +export interface IUser { + id: number; + login?: string; +} + +export class User implements IUser { + constructor(public id: number, public login: string) {} +} + +export function getUserIdentifier(user: IUser): number { + return user.id; +} diff --git a/src/main/webapp/app/entities/user/user.service.spec.ts b/src/main/webapp/app/entities/user/user.service.spec.ts new file mode 100644 index 00000000..7e7d9849 --- /dev/null +++ b/src/main/webapp/app/entities/user/user.service.spec.ts @@ -0,0 +1,109 @@ +import { TestBed } from '@angular/core/testing'; +import { HttpErrorResponse } from '@angular/common/http'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; + +import { User, IUser } from './user.model'; + +import { UserService } from './user.service'; + +describe('User Service', () => { + let service: UserService; + let httpMock: HttpTestingController; + let expectedResult: IUser | IUser[] | boolean | number | null; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + }); + expectedResult = null; + service = TestBed.inject(UserService); + httpMock = TestBed.inject(HttpTestingController); + }); + + afterEach(() => { + httpMock.verify(); + }); + + describe('Service methods', () => { + it('should return Users', () => { + service.query().subscribe(received => { + expectedResult = received.body; + }); + + const req = httpMock.expectOne({ method: 'GET' }); + req.flush([new User(123, 'user')]); + expect(expectedResult).toEqual([{ id: 123, login: 'user' }]); + }); + + it('should propagate not found response', () => { + service.query().subscribe({ + error: (error: HttpErrorResponse) => (expectedResult = error.status), + }); + + const req = httpMock.expectOne({ method: 'GET' }); + req.flush('Internal Server Error', { + status: 500, + statusText: 'Inernal Server Error', + }); + expect(expectedResult).toEqual(500); + }); + + describe('addUserToCollectionIfMissing', () => { + it('should add a User to an empty array', () => { + const user: IUser = { id: 123 }; + expectedResult = service.addUserToCollectionIfMissing([], user); + expect(expectedResult).toHaveLength(1); + expect(expectedResult).toContain(user); + }); + + it('should not add a User to an array that contains it', () => { + const user: IUser = { id: 123 }; + const userCollection: IUser[] = [ + { + ...user, + }, + { id: 456 }, + ]; + expectedResult = service.addUserToCollectionIfMissing(userCollection, user); + expect(expectedResult).toHaveLength(2); + }); + + it("should add a User to an array that doesn't contain it", () => { + const user: IUser = { id: 123 }; + const userCollection: IUser[] = [{ id: 456 }]; + expectedResult = service.addUserToCollectionIfMissing(userCollection, user); + expect(expectedResult).toHaveLength(2); + expect(expectedResult).toContain(user); + }); + + it('should add only unique User to an array', () => { + const userArray: IUser[] = [{ id: 123 }, { id: 456 }, { id: 27699 }]; + const userCollection: IUser[] = [{ id: 456 }]; + expectedResult = service.addUserToCollectionIfMissing(userCollection, ...userArray); + expect(expectedResult).toHaveLength(3); + }); + + it('should accept varargs', () => { + const user: IUser = { id: 123 }; + const user2: IUser = { id: 456 }; + expectedResult = service.addUserToCollectionIfMissing([], user, user2); + expect(expectedResult).toHaveLength(2); + expect(expectedResult).toContain(user); + expect(expectedResult).toContain(user2); + }); + + it('should accept null and undefined values', () => { + const user: IUser = { id: 123 }; + expectedResult = service.addUserToCollectionIfMissing([], null, user, undefined); + expect(expectedResult).toHaveLength(1); + expect(expectedResult).toContain(user); + }); + + it('should return initial array if no users is added', () => { + const userCollection: IUser[] = [{ id: 456 }]; + expectedResult = service.addUserToCollectionIfMissing(userCollection, null, undefined); + expect(expectedResult).toEqual(userCollection); + }); + }); + }); +}); diff --git a/src/main/webapp/app/entities/user/user.service.ts b/src/main/webapp/app/entities/user/user.service.ts new file mode 100644 index 00000000..b1daba8b --- /dev/null +++ b/src/main/webapp/app/entities/user/user.service.ts @@ -0,0 +1,45 @@ +import { Injectable } from '@angular/core'; +import { HttpClient, HttpResponse } from '@angular/common/http'; +import { Observable } from 'rxjs'; + +import { ApplicationConfigService } from 'app/core/config/application-config.service'; +import { createRequestOption } from 'app/core/request/request-util'; +import { isPresent } from 'app/core/util/operators'; +import { Pagination } from 'app/core/request/request.model'; +import { IUser, getUserIdentifier } from './user.model'; + +@Injectable({ providedIn: 'root' }) +export class UserService { + private resourceUrl = this.applicationConfigService.getEndpointFor('api/users'); + + constructor(private http: HttpClient, private applicationConfigService: ApplicationConfigService) {} + + query(req?: Pagination): Observable> { + const options = createRequestOption(req); + return this.http.get(this.resourceUrl, { params: options, observe: 'response' }); + } + + compareUser(o1: Pick | null, o2: Pick | null): boolean { + return o1 && o2 ? o1.id === o2.id : o1 === o2; + } + + addUserToCollectionIfMissing & Pick>( + userCollection: Type[], + ...usersToCheck: (Type | null | undefined)[] + ): IUser[] { + const users: Type[] = usersToCheck.filter(isPresent); + if (users.length > 0) { + const userCollectionIdentifiers = userCollection.map(userItem => getUserIdentifier(userItem)!); + const usersToAdd = users.filter(userItem => { + const userIdentifier = getUserIdentifier(userItem); + if (userCollectionIdentifiers.includes(userIdentifier)) { + return false; + } + userCollectionIdentifiers.push(userIdentifier); + return true; + }); + return [...usersToAdd, ...userCollection]; + } + return userCollection; + } +} diff --git a/src/main/webapp/app/entities/weight/delete/weight-delete-dialog.component.html b/src/main/webapp/app/entities/weight/delete/weight-delete-dialog.component.html new file mode 100644 index 00000000..5a2b3a6e --- /dev/null +++ b/src/main/webapp/app/entities/weight/delete/weight-delete-dialog.component.html @@ -0,0 +1,24 @@ +
+ + + + + +
diff --git a/src/main/webapp/app/entities/weight/delete/weight-delete-dialog.component.spec.ts b/src/main/webapp/app/entities/weight/delete/weight-delete-dialog.component.spec.ts new file mode 100644 index 00000000..fb56680b --- /dev/null +++ b/src/main/webapp/app/entities/weight/delete/weight-delete-dialog.component.spec.ts @@ -0,0 +1,63 @@ +jest.mock('@ng-bootstrap/ng-bootstrap'); + +import { ComponentFixture, TestBed, inject, fakeAsync, tick } from '@angular/core/testing'; +import { HttpResponse } from '@angular/common/http'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { of } from 'rxjs'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; + +import { WeightService } from '../service/weight.service'; + +import { WeightDeleteDialogComponent } from './weight-delete-dialog.component'; + +describe('Weight Management Delete Component', () => { + let comp: WeightDeleteDialogComponent; + let fixture: ComponentFixture; + let service: WeightService; + let mockActiveModal: NgbActiveModal; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + declarations: [WeightDeleteDialogComponent], + providers: [NgbActiveModal], + }) + .overrideTemplate(WeightDeleteDialogComponent, '') + .compileComponents(); + fixture = TestBed.createComponent(WeightDeleteDialogComponent); + comp = fixture.componentInstance; + service = TestBed.inject(WeightService); + mockActiveModal = TestBed.inject(NgbActiveModal); + }); + + describe('confirmDelete', () => { + it('Should call delete service on confirmDelete', inject( + [], + fakeAsync(() => { + // GIVEN + jest.spyOn(service, 'delete').mockReturnValue(of(new HttpResponse({ body: {} }))); + + // WHEN + comp.confirmDelete(123); + tick(); + + // THEN + expect(service.delete).toHaveBeenCalledWith(123); + expect(mockActiveModal.close).toHaveBeenCalledWith('deleted'); + }) + )); + + it('Should not call delete service on clear', () => { + // GIVEN + jest.spyOn(service, 'delete'); + + // WHEN + comp.cancel(); + + // THEN + expect(service.delete).not.toHaveBeenCalled(); + expect(mockActiveModal.close).not.toHaveBeenCalled(); + expect(mockActiveModal.dismiss).toHaveBeenCalled(); + }); + }); +}); diff --git a/src/main/webapp/app/entities/weight/delete/weight-delete-dialog.component.ts b/src/main/webapp/app/entities/weight/delete/weight-delete-dialog.component.ts new file mode 100644 index 00000000..f313c7a5 --- /dev/null +++ b/src/main/webapp/app/entities/weight/delete/weight-delete-dialog.component.ts @@ -0,0 +1,25 @@ +import { Component } from '@angular/core'; +import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; + +import { IWeight } from '../weight.model'; +import { WeightService } from '../service/weight.service'; +import { ITEM_DELETED_EVENT } from 'app/config/navigation.constants'; + +@Component({ + templateUrl: './weight-delete-dialog.component.html', +}) +export class WeightDeleteDialogComponent { + weight?: IWeight; + + constructor(protected weightService: WeightService, protected activeModal: NgbActiveModal) {} + + cancel(): void { + this.activeModal.dismiss(); + } + + confirmDelete(id: number): void { + this.weightService.delete(id).subscribe(() => { + this.activeModal.close(ITEM_DELETED_EVENT); + }); + } +} diff --git a/src/main/webapp/app/entities/weight/detail/weight-detail.component.html b/src/main/webapp/app/entities/weight/detail/weight-detail.component.html new file mode 100644 index 00000000..630b0d33 --- /dev/null +++ b/src/main/webapp/app/entities/weight/detail/weight-detail.component.html @@ -0,0 +1,40 @@ +
+
+
+

Weight

+ +
+ + + + + +
+
ID
+
+ {{ weight.id }} +
+
Timestamp
+
+ {{ weight.timestamp | formatMediumDatetime }} +
+
Weight
+
+ {{ weight.weight }} +
+
User
+
+ {{ weight.user?.login }} +
+
+ + + + +
+
+
diff --git a/src/main/webapp/app/entities/weight/detail/weight-detail.component.spec.ts b/src/main/webapp/app/entities/weight/detail/weight-detail.component.spec.ts new file mode 100644 index 00000000..a041dfc9 --- /dev/null +++ b/src/main/webapp/app/entities/weight/detail/weight-detail.component.spec.ts @@ -0,0 +1,36 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ActivatedRoute } from '@angular/router'; +import { of } from 'rxjs'; + +import { WeightDetailComponent } from './weight-detail.component'; + +describe('Weight Management Detail Component', () => { + let comp: WeightDetailComponent; + let fixture: ComponentFixture; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [WeightDetailComponent], + providers: [ + { + provide: ActivatedRoute, + useValue: { data: of({ weight: { id: 123 } }) }, + }, + ], + }) + .overrideTemplate(WeightDetailComponent, '') + .compileComponents(); + fixture = TestBed.createComponent(WeightDetailComponent); + comp = fixture.componentInstance; + }); + + describe('OnInit', () => { + it('Should load weight on init', () => { + // WHEN + comp.ngOnInit(); + + // THEN + expect(comp.weight).toEqual(expect.objectContaining({ id: 123 })); + }); + }); +}); diff --git a/src/main/webapp/app/entities/weight/detail/weight-detail.component.ts b/src/main/webapp/app/entities/weight/detail/weight-detail.component.ts new file mode 100644 index 00000000..7865c77f --- /dev/null +++ b/src/main/webapp/app/entities/weight/detail/weight-detail.component.ts @@ -0,0 +1,24 @@ +import { Component, OnInit } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; + +import { IWeight } from '../weight.model'; + +@Component({ + selector: 'jhi-weight-detail', + templateUrl: './weight-detail.component.html', +}) +export class WeightDetailComponent implements OnInit { + weight: IWeight | null = null; + + constructor(protected activatedRoute: ActivatedRoute) {} + + ngOnInit(): void { + this.activatedRoute.data.subscribe(({ weight }) => { + this.weight = weight; + }); + } + + previousState(): void { + window.history.back(); + } +} diff --git a/src/main/webapp/app/entities/weight/list/weight.component.html b/src/main/webapp/app/entities/weight/list/weight.component.html new file mode 100644 index 00000000..a8a7f686 --- /dev/null +++ b/src/main/webapp/app/entities/weight/list/weight.component.html @@ -0,0 +1,123 @@ +
+

+ Weights + +
+ + + +
+

+ + + + + +
+
+
+ + + + + + +
+
+
+ +
+ No Weights found +
+ +
+ + + + + + + + + + + + + + + + + + + +
+
+ ID + +
+
+
+ Timestamp + +
+
+
+ Weight + +
+
+
+ User + +
+
+ {{ weight.id }} + {{ weight.timestamp | formatMediumDatetime }}{{ weight.weight }} + {{ weight.user?.login }} + +
+ + + + + +
+
+
+
diff --git a/src/main/webapp/app/entities/weight/list/weight.component.spec.ts b/src/main/webapp/app/entities/weight/list/weight.component.spec.ts new file mode 100644 index 00000000..b2060c38 --- /dev/null +++ b/src/main/webapp/app/entities/weight/list/weight.component.spec.ts @@ -0,0 +1,124 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { HttpHeaders, HttpResponse } from '@angular/common/http'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { ActivatedRoute } from '@angular/router'; +import { RouterTestingModule } from '@angular/router/testing'; +import { of } from 'rxjs'; + +import { WeightService } from '../service/weight.service'; + +import { WeightComponent } from './weight.component'; +import SpyInstance = jest.SpyInstance; + +describe('Weight Management Component', () => { + let comp: WeightComponent; + let fixture: ComponentFixture; + let service: WeightService; + let routerNavigateSpy: SpyInstance>; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [RouterTestingModule.withRoutes([{ path: 'weight', component: WeightComponent }]), HttpClientTestingModule], + declarations: [WeightComponent], + providers: [ + { + provide: ActivatedRoute, + useValue: { + data: of({ + defaultSort: 'id,asc', + }), + queryParamMap: of( + jest.requireActual('@angular/router').convertToParamMap({ + page: '1', + size: '1', + sort: 'id,desc', + }) + ), + snapshot: { queryParams: {} }, + }, + }, + ], + }) + .overrideTemplate(WeightComponent, '') + .compileComponents(); + + fixture = TestBed.createComponent(WeightComponent); + comp = fixture.componentInstance; + service = TestBed.inject(WeightService); + routerNavigateSpy = jest.spyOn(comp.router, 'navigate'); + + const headers = new HttpHeaders(); + jest.spyOn(service, 'query').mockReturnValue( + of( + new HttpResponse({ + body: [{ id: 123 }], + headers, + }) + ) + ); + }); + + it('Should call load all on init', () => { + // WHEN + comp.ngOnInit(); + + // THEN + expect(service.query).toHaveBeenCalled(); + expect(comp.weights?.[0]).toEqual(expect.objectContaining({ id: 123 })); + }); + + describe('trackId', () => { + it('Should forward to weightService', () => { + const entity = { id: 123 }; + jest.spyOn(service, 'getWeightIdentifier'); + const id = comp.trackId(0, entity); + expect(service.getWeightIdentifier).toHaveBeenCalledWith(entity); + expect(id).toBe(entity.id); + }); + }); + + it('should load a page', () => { + // WHEN + comp.navigateToPage(1); + + // THEN + expect(routerNavigateSpy).toHaveBeenCalled(); + }); + + it('should calculate the sort attribute for an id', () => { + // WHEN + comp.ngOnInit(); + + // THEN + expect(service.query).toHaveBeenLastCalledWith(expect.objectContaining({ sort: ['id,desc'] })); + }); + + it('should calculate the sort attribute for a non-id attribute', () => { + // GIVEN + comp.predicate = 'name'; + + // WHEN + comp.navigateToWithComponentValues(); + + // THEN + expect(routerNavigateSpy).toHaveBeenLastCalledWith( + expect.anything(), + expect.objectContaining({ + queryParams: expect.objectContaining({ + sort: ['name,asc'], + }), + }) + ); + }); + + it('should re-initialize the page', () => { + // WHEN + comp.loadPage(1); + comp.reset(); + + // THEN + expect(comp.page).toEqual(1); + expect(service.query).toHaveBeenCalledTimes(2); + expect(comp.weights?.[0]).toEqual(expect.objectContaining({ id: 123 })); + }); +}); diff --git a/src/main/webapp/app/entities/weight/list/weight.component.ts b/src/main/webapp/app/entities/weight/list/weight.component.ts new file mode 100644 index 00000000..6210987f --- /dev/null +++ b/src/main/webapp/app/entities/weight/list/weight.component.ts @@ -0,0 +1,185 @@ +import { Component, OnInit } from '@angular/core'; +import { HttpHeaders } from '@angular/common/http'; +import { ActivatedRoute, Data, ParamMap, Router } from '@angular/router'; +import { combineLatest, filter, Observable, switchMap, tap } from 'rxjs'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; + +import { IWeight } from '../weight.model'; + +import { ITEMS_PER_PAGE } from 'app/config/pagination.constants'; +import { ASC, DESC, SORT, ITEM_DELETED_EVENT, DEFAULT_SORT_DATA } from 'app/config/navigation.constants'; +import { EntityArrayResponseType, WeightService } from '../service/weight.service'; +import { WeightDeleteDialogComponent } from '../delete/weight-delete-dialog.component'; +import { ParseLinks } from 'app/core/util/parse-links.service'; + +@Component({ + selector: 'jhi-weight', + templateUrl: './weight.component.html', +}) +export class WeightComponent implements OnInit { + weights?: IWeight[]; + isLoading = false; + + predicate = 'id'; + ascending = true; + currentSearch = ''; + + itemsPerPage = ITEMS_PER_PAGE; + links: { [key: string]: number } = { + last: 0, + }; + page = 1; + + constructor( + protected weightService: WeightService, + protected activatedRoute: ActivatedRoute, + public router: Router, + protected parseLinks: ParseLinks, + protected modalService: NgbModal + ) {} + + reset(): void { + this.page = 1; + this.weights = []; + this.load(); + } + + loadPage(page: number): void { + this.page = page; + this.load(); + } + + trackId = (_index: number, item: IWeight): number => this.weightService.getWeightIdentifier(item); + + search(query: string): void { + this.page = 1; + this.currentSearch = query; + this.navigateToWithComponentValues(); + } + + ngOnInit(): void { + this.load(); + } + + delete(weight: IWeight): void { + const modalRef = this.modalService.open(WeightDeleteDialogComponent, { size: 'lg', backdrop: 'static' }); + modalRef.componentInstance.weight = weight; + // unsubscribe not needed because closed completes on modal close + modalRef.closed + .pipe( + filter(reason => reason === ITEM_DELETED_EVENT), + switchMap(() => this.loadFromBackendWithRouteInformations()) + ) + .subscribe({ + next: (res: EntityArrayResponseType) => { + this.onResponseSuccess(res); + }, + }); + } + + load(): void { + this.loadFromBackendWithRouteInformations().subscribe({ + next: (res: EntityArrayResponseType) => { + this.onResponseSuccess(res); + }, + }); + } + + navigateToWithComponentValues(): void { + this.handleNavigation(this.page, this.predicate, this.ascending, this.currentSearch); + } + + navigateToPage(page = this.page): void { + this.handleNavigation(page, this.predicate, this.ascending, this.currentSearch); + } + + protected loadFromBackendWithRouteInformations(): Observable { + return combineLatest([this.activatedRoute.queryParamMap, this.activatedRoute.data]).pipe( + tap(([params, data]) => this.fillComponentAttributeFromRoute(params, data)), + switchMap(() => this.queryBackend(this.page, this.predicate, this.ascending, this.currentSearch)) + ); + } + + protected fillComponentAttributeFromRoute(params: ParamMap, data: Data): void { + const sort = (params.get(SORT) ?? data[DEFAULT_SORT_DATA]).split(','); + this.predicate = sort[0]; + this.ascending = sort[1] === ASC; + if (params.has('search') && params.get('search') !== '') { + this.currentSearch = params.get('search') as string; + } + } + + protected onResponseSuccess(response: EntityArrayResponseType): void { + this.fillComponentAttributesFromResponseHeader(response.headers); + const dataFromBody = this.fillComponentAttributesFromResponseBody(response.body); + this.weights = dataFromBody; + } + + protected fillComponentAttributesFromResponseBody(data: IWeight[] | null): IWeight[] { + const weightsNew = this.weights ?? []; + if (data) { + for (const d of data) { + if (weightsNew.map(op => op.id).indexOf(d.id) === -1) { + weightsNew.push(d); + } + } + } + return weightsNew; + } + + protected fillComponentAttributesFromResponseHeader(headers: HttpHeaders): void { + const linkHeader = headers.get('link'); + if (linkHeader) { + this.links = this.parseLinks.parse(linkHeader); + } else { + this.links = { + last: 0, + }; + } + } + + protected queryBackend( + page?: number, + predicate?: string, + ascending?: boolean, + currentSearch?: string + ): Observable { + this.isLoading = true; + const pageToLoad: number = page ?? 1; + const queryObject: any = { + page: pageToLoad - 1, + size: this.itemsPerPage, + eagerload: true, + query: currentSearch, + sort: this.getSortQueryParam(predicate, ascending), + }; + if (this.currentSearch && this.currentSearch !== '') { + return this.weightService.search(queryObject).pipe(tap(() => (this.isLoading = false))); + } else { + return this.weightService.query(queryObject).pipe(tap(() => (this.isLoading = false))); + } + } + + protected handleNavigation(page = this.page, predicate?: string, ascending?: boolean, currentSearch?: string): void { + const queryParamsObj = { + search: currentSearch, + page, + size: this.itemsPerPage, + sort: this.getSortQueryParam(predicate, ascending), + }; + + this.router.navigate(['./'], { + relativeTo: this.activatedRoute, + queryParams: queryParamsObj, + }); + } + + protected getSortQueryParam(predicate = this.predicate, ascending = this.ascending): string[] { + const ascendingQueryParam = ascending ? ASC : DESC; + if (predicate === '') { + return []; + } else { + return [predicate + ',' + ascendingQueryParam]; + } + } +} diff --git a/src/main/webapp/app/entities/weight/route/weight-routing-resolve.service.spec.ts b/src/main/webapp/app/entities/weight/route/weight-routing-resolve.service.spec.ts new file mode 100644 index 00000000..15b89032 --- /dev/null +++ b/src/main/webapp/app/entities/weight/route/weight-routing-resolve.service.spec.ts @@ -0,0 +1,89 @@ +import { TestBed } from '@angular/core/testing'; +import { HttpResponse } from '@angular/common/http'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { ActivatedRouteSnapshot, ActivatedRoute, Router, convertToParamMap } from '@angular/router'; +import { RouterTestingModule } from '@angular/router/testing'; +import { of } from 'rxjs'; + +import { IWeight } from '../weight.model'; +import { WeightService } from '../service/weight.service'; + +import { WeightRoutingResolveService } from './weight-routing-resolve.service'; + +describe('Weight routing resolve service', () => { + let mockRouter: Router; + let mockActivatedRouteSnapshot: ActivatedRouteSnapshot; + let routingResolveService: WeightRoutingResolveService; + let service: WeightService; + let resultWeight: IWeight | null | undefined; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule, RouterTestingModule.withRoutes([])], + providers: [ + { + provide: ActivatedRoute, + useValue: { + snapshot: { + paramMap: convertToParamMap({}), + }, + }, + }, + ], + }); + mockRouter = TestBed.inject(Router); + jest.spyOn(mockRouter, 'navigate').mockImplementation(() => Promise.resolve(true)); + mockActivatedRouteSnapshot = TestBed.inject(ActivatedRoute).snapshot; + routingResolveService = TestBed.inject(WeightRoutingResolveService); + service = TestBed.inject(WeightService); + resultWeight = undefined; + }); + + describe('resolve', () => { + it('should return IWeight returned by find', () => { + // GIVEN + service.find = jest.fn(id => of(new HttpResponse({ body: { id } }))); + mockActivatedRouteSnapshot.params = { id: 123 }; + + // WHEN + routingResolveService.resolve(mockActivatedRouteSnapshot).subscribe(result => { + resultWeight = result; + }); + + // THEN + expect(service.find).toBeCalledWith(123); + expect(resultWeight).toEqual({ id: 123 }); + }); + + it('should return null if id is not provided', () => { + // GIVEN + service.find = jest.fn(); + mockActivatedRouteSnapshot.params = {}; + + // WHEN + routingResolveService.resolve(mockActivatedRouteSnapshot).subscribe(result => { + resultWeight = result; + }); + + // THEN + expect(service.find).not.toBeCalled(); + expect(resultWeight).toEqual(null); + }); + + it('should route to 404 page if data not found in server', () => { + // GIVEN + jest.spyOn(service, 'find').mockReturnValue(of(new HttpResponse({ body: null }))); + mockActivatedRouteSnapshot.params = { id: 123 }; + + // WHEN + routingResolveService.resolve(mockActivatedRouteSnapshot).subscribe(result => { + resultWeight = result; + }); + + // THEN + expect(service.find).toBeCalledWith(123); + expect(resultWeight).toEqual(undefined); + expect(mockRouter.navigate).toHaveBeenCalledWith(['404']); + }); + }); +}); diff --git a/src/main/webapp/app/entities/weight/route/weight-routing-resolve.service.ts b/src/main/webapp/app/entities/weight/route/weight-routing-resolve.service.ts new file mode 100644 index 00000000..ec390d5b --- /dev/null +++ b/src/main/webapp/app/entities/weight/route/weight-routing-resolve.service.ts @@ -0,0 +1,30 @@ +import { Injectable } from '@angular/core'; +import { HttpResponse } from '@angular/common/http'; +import { Resolve, ActivatedRouteSnapshot, Router } from '@angular/router'; +import { Observable, of, EMPTY } from 'rxjs'; +import { mergeMap } from 'rxjs/operators'; + +import { IWeight } from '../weight.model'; +import { WeightService } from '../service/weight.service'; + +@Injectable({ providedIn: 'root' }) +export class WeightRoutingResolveService implements Resolve { + constructor(protected service: WeightService, protected router: Router) {} + + resolve(route: ActivatedRouteSnapshot): Observable { + const id = route.params['id']; + if (id) { + return this.service.find(id).pipe( + mergeMap((weight: HttpResponse) => { + if (weight.body) { + return of(weight.body); + } else { + this.router.navigate(['404']); + return EMPTY; + } + }) + ); + } + return of(null); + } +} diff --git a/src/main/webapp/app/entities/weight/route/weight-routing.module.ts b/src/main/webapp/app/entities/weight/route/weight-routing.module.ts new file mode 100644 index 00000000..beca3c8a --- /dev/null +++ b/src/main/webapp/app/entities/weight/route/weight-routing.module.ts @@ -0,0 +1,50 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; + +import { UserRouteAccessService } from 'app/core/auth/user-route-access.service'; +import { WeightComponent } from '../list/weight.component'; +import { WeightDetailComponent } from '../detail/weight-detail.component'; +import { WeightUpdateComponent } from '../update/weight-update.component'; +import { WeightRoutingResolveService } from './weight-routing-resolve.service'; +import { ASC } from 'app/config/navigation.constants'; + +const weightRoute: Routes = [ + { + path: '', + component: WeightComponent, + data: { + defaultSort: 'id,' + ASC, + }, + canActivate: [UserRouteAccessService], + }, + { + path: ':id/view', + component: WeightDetailComponent, + resolve: { + weight: WeightRoutingResolveService, + }, + canActivate: [UserRouteAccessService], + }, + { + path: 'new', + component: WeightUpdateComponent, + resolve: { + weight: WeightRoutingResolveService, + }, + canActivate: [UserRouteAccessService], + }, + { + path: ':id/edit', + component: WeightUpdateComponent, + resolve: { + weight: WeightRoutingResolveService, + }, + canActivate: [UserRouteAccessService], + }, +]; + +@NgModule({ + imports: [RouterModule.forChild(weightRoute)], + exports: [RouterModule], +}) +export class WeightRoutingModule {} diff --git a/src/main/webapp/app/entities/weight/service/weight.service.spec.ts b/src/main/webapp/app/entities/weight/service/weight.service.spec.ts new file mode 100644 index 00000000..a5f18d46 --- /dev/null +++ b/src/main/webapp/app/entities/weight/service/weight.service.spec.ts @@ -0,0 +1,206 @@ +import { TestBed } from '@angular/core/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; + +import { IWeight } from '../weight.model'; +import { sampleWithRequiredData, sampleWithNewData, sampleWithPartialData, sampleWithFullData } from '../weight.test-samples'; + +import { WeightService, RestWeight } from './weight.service'; + +const requireRestSample: RestWeight = { + ...sampleWithRequiredData, + timestamp: sampleWithRequiredData.timestamp?.toJSON(), +}; + +describe('Weight Service', () => { + let service: WeightService; + let httpMock: HttpTestingController; + let expectedResult: IWeight | IWeight[] | boolean | null; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + }); + expectedResult = null; + service = TestBed.inject(WeightService); + httpMock = TestBed.inject(HttpTestingController); + }); + + describe('Service methods', () => { + it('should find an element', () => { + const returnedFromService = { ...requireRestSample }; + const expected = { ...sampleWithRequiredData }; + + service.find(123).subscribe(resp => (expectedResult = resp.body)); + + const req = httpMock.expectOne({ method: 'GET' }); + req.flush(returnedFromService); + expect(expectedResult).toMatchObject(expected); + }); + + it('should create a Weight', () => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const weight = { ...sampleWithNewData }; + const returnedFromService = { ...requireRestSample }; + const expected = { ...sampleWithRequiredData }; + + service.create(weight).subscribe(resp => (expectedResult = resp.body)); + + const req = httpMock.expectOne({ method: 'POST' }); + req.flush(returnedFromService); + expect(expectedResult).toMatchObject(expected); + }); + + it('should update a Weight', () => { + const weight = { ...sampleWithRequiredData }; + const returnedFromService = { ...requireRestSample }; + const expected = { ...sampleWithRequiredData }; + + service.update(weight).subscribe(resp => (expectedResult = resp.body)); + + const req = httpMock.expectOne({ method: 'PUT' }); + req.flush(returnedFromService); + expect(expectedResult).toMatchObject(expected); + }); + + it('should partial update a Weight', () => { + const patchObject = { ...sampleWithPartialData }; + const returnedFromService = { ...requireRestSample }; + const expected = { ...sampleWithRequiredData }; + + service.partialUpdate(patchObject).subscribe(resp => (expectedResult = resp.body)); + + const req = httpMock.expectOne({ method: 'PATCH' }); + req.flush(returnedFromService); + expect(expectedResult).toMatchObject(expected); + }); + + it('should return a list of Weight', () => { + const returnedFromService = { ...requireRestSample }; + + const expected = { ...sampleWithRequiredData }; + + service.query().subscribe(resp => (expectedResult = resp.body)); + + const req = httpMock.expectOne({ method: 'GET' }); + req.flush([returnedFromService]); + httpMock.verify(); + expect(expectedResult).toMatchObject([expected]); + }); + + it('should delete a Weight', () => { + const expected = true; + + service.delete(123).subscribe(resp => (expectedResult = resp.ok)); + + const req = httpMock.expectOne({ method: 'DELETE' }); + req.flush({ status: 200 }); + expect(expectedResult).toBe(expected); + }); + + describe('addWeightToCollectionIfMissing', () => { + it('should add a Weight to an empty array', () => { + const weight: IWeight = sampleWithRequiredData; + expectedResult = service.addWeightToCollectionIfMissing([], weight); + expect(expectedResult).toHaveLength(1); + expect(expectedResult).toContain(weight); + }); + + it('should not add a Weight to an array that contains it', () => { + const weight: IWeight = sampleWithRequiredData; + const weightCollection: IWeight[] = [ + { + ...weight, + }, + sampleWithPartialData, + ]; + expectedResult = service.addWeightToCollectionIfMissing(weightCollection, weight); + expect(expectedResult).toHaveLength(2); + }); + + it("should add a Weight to an array that doesn't contain it", () => { + const weight: IWeight = sampleWithRequiredData; + const weightCollection: IWeight[] = [sampleWithPartialData]; + expectedResult = service.addWeightToCollectionIfMissing(weightCollection, weight); + expect(expectedResult).toHaveLength(2); + expect(expectedResult).toContain(weight); + }); + + it('should add only unique Weight to an array', () => { + const weightArray: IWeight[] = [sampleWithRequiredData, sampleWithPartialData, sampleWithFullData]; + const weightCollection: IWeight[] = [sampleWithRequiredData]; + expectedResult = service.addWeightToCollectionIfMissing(weightCollection, ...weightArray); + expect(expectedResult).toHaveLength(3); + }); + + it('should accept varargs', () => { + const weight: IWeight = sampleWithRequiredData; + const weight2: IWeight = sampleWithPartialData; + expectedResult = service.addWeightToCollectionIfMissing([], weight, weight2); + expect(expectedResult).toHaveLength(2); + expect(expectedResult).toContain(weight); + expect(expectedResult).toContain(weight2); + }); + + it('should accept null and undefined values', () => { + const weight: IWeight = sampleWithRequiredData; + expectedResult = service.addWeightToCollectionIfMissing([], null, weight, undefined); + expect(expectedResult).toHaveLength(1); + expect(expectedResult).toContain(weight); + }); + + it('should return initial array if no Weight is added', () => { + const weightCollection: IWeight[] = [sampleWithRequiredData]; + expectedResult = service.addWeightToCollectionIfMissing(weightCollection, undefined, null); + expect(expectedResult).toEqual(weightCollection); + }); + }); + + describe('compareWeight', () => { + it('Should return true if both entities are null', () => { + const entity1 = null; + const entity2 = null; + + const compareResult = service.compareWeight(entity1, entity2); + + expect(compareResult).toEqual(true); + }); + + it('Should return false if one entity is null', () => { + const entity1 = { id: 123 }; + const entity2 = null; + + const compareResult1 = service.compareWeight(entity1, entity2); + const compareResult2 = service.compareWeight(entity2, entity1); + + expect(compareResult1).toEqual(false); + expect(compareResult2).toEqual(false); + }); + + it('Should return false if primaryKey differs', () => { + const entity1 = { id: 123 }; + const entity2 = { id: 456 }; + + const compareResult1 = service.compareWeight(entity1, entity2); + const compareResult2 = service.compareWeight(entity2, entity1); + + expect(compareResult1).toEqual(false); + expect(compareResult2).toEqual(false); + }); + + it('Should return false if primaryKey matches', () => { + const entity1 = { id: 123 }; + const entity2 = { id: 123 }; + + const compareResult1 = service.compareWeight(entity1, entity2); + const compareResult2 = service.compareWeight(entity2, entity1); + + expect(compareResult1).toEqual(true); + expect(compareResult2).toEqual(true); + }); + }); + }); + + afterEach(() => { + httpMock.verify(); + }); +}); diff --git a/src/main/webapp/app/entities/weight/service/weight.service.ts b/src/main/webapp/app/entities/weight/service/weight.service.ts new file mode 100644 index 00000000..125ac95b --- /dev/null +++ b/src/main/webapp/app/entities/weight/service/weight.service.ts @@ -0,0 +1,133 @@ +import { Injectable } from '@angular/core'; +import { HttpClient, HttpResponse } from '@angular/common/http'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; +import dayjs from 'dayjs/esm'; + +import { isPresent } from 'app/core/util/operators'; +import { ApplicationConfigService } from 'app/core/config/application-config.service'; +import { createRequestOption } from 'app/core/request/request-util'; +import { SearchWithPagination } from 'app/core/request/request.model'; +import { IWeight, NewWeight } from '../weight.model'; + +export type PartialUpdateWeight = Partial & Pick; + +type RestOf = Omit & { + timestamp?: string | null; +}; + +export type RestWeight = RestOf; + +export type NewRestWeight = RestOf; + +export type PartialUpdateRestWeight = RestOf; + +export type EntityResponseType = HttpResponse; +export type EntityArrayResponseType = HttpResponse; + +@Injectable({ providedIn: 'root' }) +export class WeightService { + protected resourceUrl = this.applicationConfigService.getEndpointFor('api/weights'); + protected resourceSearchUrl = this.applicationConfigService.getEndpointFor('api/_search/weights'); + + constructor(protected http: HttpClient, protected applicationConfigService: ApplicationConfigService) {} + + create(weight: NewWeight): Observable { + const copy = this.convertDateFromClient(weight); + return this.http + .post(this.resourceUrl, copy, { observe: 'response' }) + .pipe(map(res => this.convertResponseFromServer(res))); + } + + update(weight: IWeight): Observable { + const copy = this.convertDateFromClient(weight); + return this.http + .put(`${this.resourceUrl}/${this.getWeightIdentifier(weight)}`, copy, { observe: 'response' }) + .pipe(map(res => this.convertResponseFromServer(res))); + } + + partialUpdate(weight: PartialUpdateWeight): Observable { + const copy = this.convertDateFromClient(weight); + return this.http + .patch(`${this.resourceUrl}/${this.getWeightIdentifier(weight)}`, copy, { observe: 'response' }) + .pipe(map(res => this.convertResponseFromServer(res))); + } + + find(id: number): Observable { + return this.http + .get(`${this.resourceUrl}/${id}`, { observe: 'response' }) + .pipe(map(res => this.convertResponseFromServer(res))); + } + + query(req?: any): Observable { + const options = createRequestOption(req); + return this.http + .get(this.resourceUrl, { params: options, observe: 'response' }) + .pipe(map(res => this.convertResponseArrayFromServer(res))); + } + + delete(id: number): Observable> { + return this.http.delete(`${this.resourceUrl}/${id}`, { observe: 'response' }); + } + + search(req: SearchWithPagination): Observable { + const options = createRequestOption(req); + return this.http + .get(this.resourceSearchUrl, { params: options, observe: 'response' }) + .pipe(map(res => this.convertResponseArrayFromServer(res))); + } + + getWeightIdentifier(weight: Pick): number { + return weight.id; + } + + compareWeight(o1: Pick | null, o2: Pick | null): boolean { + return o1 && o2 ? this.getWeightIdentifier(o1) === this.getWeightIdentifier(o2) : o1 === o2; + } + + addWeightToCollectionIfMissing>( + weightCollection: Type[], + ...weightsToCheck: (Type | null | undefined)[] + ): Type[] { + const weights: Type[] = weightsToCheck.filter(isPresent); + if (weights.length > 0) { + const weightCollectionIdentifiers = weightCollection.map(weightItem => this.getWeightIdentifier(weightItem)!); + const weightsToAdd = weights.filter(weightItem => { + const weightIdentifier = this.getWeightIdentifier(weightItem); + if (weightCollectionIdentifiers.includes(weightIdentifier)) { + return false; + } + weightCollectionIdentifiers.push(weightIdentifier); + return true; + }); + return [...weightsToAdd, ...weightCollection]; + } + return weightCollection; + } + + protected convertDateFromClient(weight: T): RestOf { + return { + ...weight, + timestamp: weight.timestamp?.toJSON() ?? null, + }; + } + + protected convertDateFromServer(restWeight: RestWeight): IWeight { + return { + ...restWeight, + timestamp: restWeight.timestamp ? dayjs(restWeight.timestamp) : undefined, + }; + } + + protected convertResponseFromServer(res: HttpResponse): HttpResponse { + return res.clone({ + body: res.body ? this.convertDateFromServer(res.body) : null, + }); + } + + protected convertResponseArrayFromServer(res: HttpResponse): HttpResponse { + return res.clone({ + body: res.body ? res.body.map(item => this.convertDateFromServer(item)) : null, + }); + } +} diff --git a/src/main/webapp/app/entities/weight/update/weight-form.service.spec.ts b/src/main/webapp/app/entities/weight/update/weight-form.service.spec.ts new file mode 100644 index 00000000..00d649fa --- /dev/null +++ b/src/main/webapp/app/entities/weight/update/weight-form.service.spec.ts @@ -0,0 +1,91 @@ +import { TestBed } from '@angular/core/testing'; + +import { sampleWithRequiredData, sampleWithNewData } from '../weight.test-samples'; + +import { WeightFormService } from './weight-form.service'; + +describe('Weight Form Service', () => { + let service: WeightFormService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(WeightFormService); + }); + + describe('Service methods', () => { + describe('createWeightFormGroup', () => { + it('should create a new form with FormControl', () => { + const formGroup = service.createWeightFormGroup(); + + expect(formGroup.controls).toEqual( + expect.objectContaining({ + id: expect.any(Object), + timestamp: expect.any(Object), + weight: expect.any(Object), + user: expect.any(Object), + }) + ); + }); + + it('passing IWeight should create a new form with FormGroup', () => { + const formGroup = service.createWeightFormGroup(sampleWithRequiredData); + + expect(formGroup.controls).toEqual( + expect.objectContaining({ + id: expect.any(Object), + timestamp: expect.any(Object), + weight: expect.any(Object), + user: expect.any(Object), + }) + ); + }); + }); + + describe('getWeight', () => { + it('should return NewWeight for default Weight initial value', () => { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const formGroup = service.createWeightFormGroup(sampleWithNewData); + + const weight = service.getWeight(formGroup) as any; + + expect(weight).toMatchObject(sampleWithNewData); + }); + + it('should return NewWeight for empty Weight initial value', () => { + const formGroup = service.createWeightFormGroup(); + + const weight = service.getWeight(formGroup) as any; + + expect(weight).toMatchObject({}); + }); + + it('should return IWeight', () => { + const formGroup = service.createWeightFormGroup(sampleWithRequiredData); + + const weight = service.getWeight(formGroup) as any; + + expect(weight).toMatchObject(sampleWithRequiredData); + }); + }); + + describe('resetForm', () => { + it('passing IWeight should not enable id FormControl', () => { + const formGroup = service.createWeightFormGroup(); + expect(formGroup.controls.id.disabled).toBe(true); + + service.resetForm(formGroup, sampleWithRequiredData); + + expect(formGroup.controls.id.disabled).toBe(true); + }); + + it('passing NewWeight should disable id FormControl', () => { + const formGroup = service.createWeightFormGroup(sampleWithRequiredData); + expect(formGroup.controls.id.disabled).toBe(true); + + service.resetForm(formGroup, { id: null }); + + expect(formGroup.controls.id.disabled).toBe(true); + }); + }); + }); +}); diff --git a/src/main/webapp/app/entities/weight/update/weight-form.service.ts b/src/main/webapp/app/entities/weight/update/weight-form.service.ts new file mode 100644 index 00000000..bd5953b9 --- /dev/null +++ b/src/main/webapp/app/entities/weight/update/weight-form.service.ts @@ -0,0 +1,104 @@ +import { Injectable } from '@angular/core'; +import { FormGroup, FormControl, Validators } from '@angular/forms'; + +import dayjs from 'dayjs/esm'; +import { DATE_TIME_FORMAT } from 'app/config/input.constants'; +import { IWeight, NewWeight } from '../weight.model'; + +/** + * A partial Type with required key is used as form input. + */ +type PartialWithRequiredKeyOf = Partial> & { id: T['id'] }; + +/** + * Type for createFormGroup and resetForm argument. + * It accepts IWeight for edit and NewWeightFormGroupInput for create. + */ +type WeightFormGroupInput = IWeight | PartialWithRequiredKeyOf; + +/** + * Type that converts some properties for forms. + */ +type FormValueOf = Omit & { + timestamp?: string | null; +}; + +type WeightFormRawValue = FormValueOf; + +type NewWeightFormRawValue = FormValueOf; + +type WeightFormDefaults = Pick; + +type WeightFormGroupContent = { + id: FormControl; + timestamp: FormControl; + weight: FormControl; + user: FormControl; +}; + +export type WeightFormGroup = FormGroup; + +@Injectable({ providedIn: 'root' }) +export class WeightFormService { + createWeightFormGroup(weight: WeightFormGroupInput = { id: null }): WeightFormGroup { + const weightRawValue = this.convertWeightToWeightRawValue({ + ...this.getFormDefaults(), + ...weight, + }); + return new FormGroup({ + id: new FormControl( + { value: weightRawValue.id, disabled: true }, + { + nonNullable: true, + validators: [Validators.required], + } + ), + timestamp: new FormControl(weightRawValue.timestamp, { + validators: [Validators.required], + }), + weight: new FormControl(weightRawValue.weight, { + validators: [Validators.required], + }), + user: new FormControl(weightRawValue.user), + }); + } + + getWeight(form: WeightFormGroup): IWeight | NewWeight { + return this.convertWeightRawValueToWeight(form.getRawValue() as WeightFormRawValue | NewWeightFormRawValue); + } + + resetForm(form: WeightFormGroup, weight: WeightFormGroupInput): void { + const weightRawValue = this.convertWeightToWeightRawValue({ ...this.getFormDefaults(), ...weight }); + form.reset( + { + ...weightRawValue, + id: { value: weightRawValue.id, disabled: true }, + } as any /* cast to workaround https://github.com/angular/angular/issues/46458 */ + ); + } + + private getFormDefaults(): WeightFormDefaults { + const currentTime = dayjs(); + + return { + id: null, + timestamp: currentTime, + }; + } + + private convertWeightRawValueToWeight(rawWeight: WeightFormRawValue | NewWeightFormRawValue): IWeight | NewWeight { + return { + ...rawWeight, + timestamp: dayjs(rawWeight.timestamp, DATE_TIME_FORMAT), + }; + } + + private convertWeightToWeightRawValue( + weight: IWeight | (Partial & WeightFormDefaults) + ): WeightFormRawValue | PartialWithRequiredKeyOf { + return { + ...weight, + timestamp: weight.timestamp ? weight.timestamp.format(DATE_TIME_FORMAT) : undefined, + }; + } +} diff --git a/src/main/webapp/app/entities/weight/update/weight-update.component.html b/src/main/webapp/app/entities/weight/update/weight-update.component.html new file mode 100644 index 00000000..c2209544 --- /dev/null +++ b/src/main/webapp/app/entities/weight/update/weight-update.component.html @@ -0,0 +1,86 @@ +
+
+
+

+ Create or edit a Weight +

+ +
+ + +
+ + +
+ +
+ +
+ +
+
+ + This field is required. + + + This field should be a date and time. + +
+
+ +
+ + +
+ + This field is required. + + + This field should be a number. + +
+
+ +
+ + +
+
+ +
+ + + +
+
+
+
diff --git a/src/main/webapp/app/entities/weight/update/weight-update.component.spec.ts b/src/main/webapp/app/entities/weight/update/weight-update.component.spec.ts new file mode 100644 index 00000000..939e797a --- /dev/null +++ b/src/main/webapp/app/entities/weight/update/weight-update.component.spec.ts @@ -0,0 +1,167 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { HttpResponse } from '@angular/common/http'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { FormBuilder } from '@angular/forms'; +import { ActivatedRoute } from '@angular/router'; +import { RouterTestingModule } from '@angular/router/testing'; +import { of, Subject, from } from 'rxjs'; + +import { WeightFormService } from './weight-form.service'; +import { WeightService } from '../service/weight.service'; +import { IWeight } from '../weight.model'; + +import { IUser } from 'app/entities/user/user.model'; +import { UserService } from 'app/entities/user/user.service'; + +import { WeightUpdateComponent } from './weight-update.component'; + +describe('Weight Management Update Component', () => { + let comp: WeightUpdateComponent; + let fixture: ComponentFixture; + let activatedRoute: ActivatedRoute; + let weightFormService: WeightFormService; + let weightService: WeightService; + let userService: UserService; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule, RouterTestingModule.withRoutes([])], + declarations: [WeightUpdateComponent], + providers: [ + FormBuilder, + { + provide: ActivatedRoute, + useValue: { + params: from([{}]), + }, + }, + ], + }) + .overrideTemplate(WeightUpdateComponent, '') + .compileComponents(); + + fixture = TestBed.createComponent(WeightUpdateComponent); + activatedRoute = TestBed.inject(ActivatedRoute); + weightFormService = TestBed.inject(WeightFormService); + weightService = TestBed.inject(WeightService); + userService = TestBed.inject(UserService); + + comp = fixture.componentInstance; + }); + + describe('ngOnInit', () => { + it('Should call User query and add missing value', () => { + const weight: IWeight = { id: 456 }; + const user: IUser = { id: 19261 }; + weight.user = user; + + const userCollection: IUser[] = [{ id: 48569 }]; + jest.spyOn(userService, 'query').mockReturnValue(of(new HttpResponse({ body: userCollection }))); + const additionalUsers = [user]; + const expectedCollection: IUser[] = [...additionalUsers, ...userCollection]; + jest.spyOn(userService, 'addUserToCollectionIfMissing').mockReturnValue(expectedCollection); + + activatedRoute.data = of({ weight }); + comp.ngOnInit(); + + expect(userService.query).toHaveBeenCalled(); + expect(userService.addUserToCollectionIfMissing).toHaveBeenCalledWith( + userCollection, + ...additionalUsers.map(expect.objectContaining) + ); + expect(comp.usersSharedCollection).toEqual(expectedCollection); + }); + + it('Should update editForm', () => { + const weight: IWeight = { id: 456 }; + const user: IUser = { id: 30575 }; + weight.user = user; + + activatedRoute.data = of({ weight }); + comp.ngOnInit(); + + expect(comp.usersSharedCollection).toContain(user); + expect(comp.weight).toEqual(weight); + }); + }); + + describe('save', () => { + it('Should call update service on save for existing entity', () => { + // GIVEN + const saveSubject = new Subject>(); + const weight = { id: 123 }; + jest.spyOn(weightFormService, 'getWeight').mockReturnValue(weight); + jest.spyOn(weightService, 'update').mockReturnValue(saveSubject); + jest.spyOn(comp, 'previousState'); + activatedRoute.data = of({ weight }); + comp.ngOnInit(); + + // WHEN + comp.save(); + expect(comp.isSaving).toEqual(true); + saveSubject.next(new HttpResponse({ body: weight })); + saveSubject.complete(); + + // THEN + expect(weightFormService.getWeight).toHaveBeenCalled(); + expect(comp.previousState).toHaveBeenCalled(); + expect(weightService.update).toHaveBeenCalledWith(expect.objectContaining(weight)); + expect(comp.isSaving).toEqual(false); + }); + + it('Should call create service on save for new entity', () => { + // GIVEN + const saveSubject = new Subject>(); + const weight = { id: 123 }; + jest.spyOn(weightFormService, 'getWeight').mockReturnValue({ id: null }); + jest.spyOn(weightService, 'create').mockReturnValue(saveSubject); + jest.spyOn(comp, 'previousState'); + activatedRoute.data = of({ weight: null }); + comp.ngOnInit(); + + // WHEN + comp.save(); + expect(comp.isSaving).toEqual(true); + saveSubject.next(new HttpResponse({ body: weight })); + saveSubject.complete(); + + // THEN + expect(weightFormService.getWeight).toHaveBeenCalled(); + expect(weightService.create).toHaveBeenCalled(); + expect(comp.isSaving).toEqual(false); + expect(comp.previousState).toHaveBeenCalled(); + }); + + it('Should set isSaving to false on error', () => { + // GIVEN + const saveSubject = new Subject>(); + const weight = { id: 123 }; + jest.spyOn(weightService, 'update').mockReturnValue(saveSubject); + jest.spyOn(comp, 'previousState'); + activatedRoute.data = of({ weight }); + comp.ngOnInit(); + + // WHEN + comp.save(); + expect(comp.isSaving).toEqual(true); + saveSubject.error('This is an error!'); + + // THEN + expect(weightService.update).toHaveBeenCalled(); + expect(comp.isSaving).toEqual(false); + expect(comp.previousState).not.toHaveBeenCalled(); + }); + }); + + describe('Compare relationships', () => { + describe('compareUser', () => { + it('Should forward to userService', () => { + const entity = { id: 123 }; + const entity2 = { id: 456 }; + jest.spyOn(userService, 'compareUser'); + comp.compareUser(entity, entity2); + expect(userService.compareUser).toHaveBeenCalledWith(entity, entity2); + }); + }); + }); +}); diff --git a/src/main/webapp/app/entities/weight/update/weight-update.component.ts b/src/main/webapp/app/entities/weight/update/weight-update.component.ts new file mode 100644 index 00000000..d1592fba --- /dev/null +++ b/src/main/webapp/app/entities/weight/update/weight-update.component.ts @@ -0,0 +1,92 @@ +import { Component, OnInit } from '@angular/core'; +import { HttpResponse } from '@angular/common/http'; +import { ActivatedRoute } from '@angular/router'; +import { Observable } from 'rxjs'; +import { finalize, map } from 'rxjs/operators'; + +import { WeightFormService, WeightFormGroup } from './weight-form.service'; +import { IWeight } from '../weight.model'; +import { WeightService } from '../service/weight.service'; +import { IUser } from 'app/entities/user/user.model'; +import { UserService } from 'app/entities/user/user.service'; + +@Component({ + selector: 'jhi-weight-update', + templateUrl: './weight-update.component.html', +}) +export class WeightUpdateComponent implements OnInit { + isSaving = false; + weight: IWeight | null = null; + + usersSharedCollection: IUser[] = []; + + editForm: WeightFormGroup = this.weightFormService.createWeightFormGroup(); + + constructor( + protected weightService: WeightService, + protected weightFormService: WeightFormService, + protected userService: UserService, + protected activatedRoute: ActivatedRoute + ) {} + + compareUser = (o1: IUser | null, o2: IUser | null): boolean => this.userService.compareUser(o1, o2); + + ngOnInit(): void { + this.activatedRoute.data.subscribe(({ weight }) => { + this.weight = weight; + if (weight) { + this.updateForm(weight); + } + + this.loadRelationshipsOptions(); + }); + } + + previousState(): void { + window.history.back(); + } + + save(): void { + this.isSaving = true; + const weight = this.weightFormService.getWeight(this.editForm); + if (weight.id !== null) { + this.subscribeToSaveResponse(this.weightService.update(weight)); + } else { + this.subscribeToSaveResponse(this.weightService.create(weight)); + } + } + + protected subscribeToSaveResponse(result: Observable>): void { + result.pipe(finalize(() => this.onSaveFinalize())).subscribe({ + next: () => this.onSaveSuccess(), + error: () => this.onSaveError(), + }); + } + + protected onSaveSuccess(): void { + this.previousState(); + } + + protected onSaveError(): void { + // Api for inheritance. + } + + protected onSaveFinalize(): void { + this.isSaving = false; + } + + protected updateForm(weight: IWeight): void { + this.weight = weight; + this.weightFormService.resetForm(this.editForm, weight); + + this.usersSharedCollection = this.userService.addUserToCollectionIfMissing(this.usersSharedCollection, weight.user); + } + + protected loadRelationshipsOptions(): void { + this.userService + .query() + .pipe(map((res: HttpResponse) => res.body ?? [])) + .pipe(map((users: IUser[]) => this.userService.addUserToCollectionIfMissing(users, this.weight?.user))) + .subscribe((users: IUser[]) => (this.usersSharedCollection = users)); + } +} diff --git a/src/main/webapp/app/entities/weight/weight.model.ts b/src/main/webapp/app/entities/weight/weight.model.ts new file mode 100644 index 00000000..8ec78bbc --- /dev/null +++ b/src/main/webapp/app/entities/weight/weight.model.ts @@ -0,0 +1,11 @@ +import dayjs from 'dayjs/esm'; +import { IUser } from 'app/entities/user/user.model'; + +export interface IWeight { + id: number; + timestamp?: dayjs.Dayjs | null; + weight?: number | null; + user?: Pick | null; +} + +export type NewWeight = Omit & { id: null }; diff --git a/src/main/webapp/app/entities/weight/weight.module.ts b/src/main/webapp/app/entities/weight/weight.module.ts new file mode 100644 index 00000000..e086e7d0 --- /dev/null +++ b/src/main/webapp/app/entities/weight/weight.module.ts @@ -0,0 +1,13 @@ +import { NgModule } from '@angular/core'; +import { SharedModule } from 'app/shared/shared.module'; +import { WeightComponent } from './list/weight.component'; +import { WeightDetailComponent } from './detail/weight-detail.component'; +import { WeightUpdateComponent } from './update/weight-update.component'; +import { WeightDeleteDialogComponent } from './delete/weight-delete-dialog.component'; +import { WeightRoutingModule } from './route/weight-routing.module'; + +@NgModule({ + imports: [SharedModule, WeightRoutingModule], + declarations: [WeightComponent, WeightDetailComponent, WeightUpdateComponent, WeightDeleteDialogComponent], +}) +export class WeightModule {} diff --git a/src/main/webapp/app/entities/weight/weight.test-samples.ts b/src/main/webapp/app/entities/weight/weight.test-samples.ts new file mode 100644 index 00000000..b46702b9 --- /dev/null +++ b/src/main/webapp/app/entities/weight/weight.test-samples.ts @@ -0,0 +1,32 @@ +import dayjs from 'dayjs/esm'; + +import { IWeight, NewWeight } from './weight.model'; + +export const sampleWithRequiredData: IWeight = { + id: 86192, + timestamp: dayjs('2022-11-07T08:31'), + weight: 42379, +}; + +export const sampleWithPartialData: IWeight = { + id: 53022, + timestamp: dayjs('2022-11-07T07:54'), + weight: 24033, +}; + +export const sampleWithFullData: IWeight = { + id: 69421, + timestamp: dayjs('2022-11-07T10:51'), + weight: 98529, +}; + +export const sampleWithNewData: NewWeight = { + timestamp: dayjs('2022-11-07T02:56'), + weight: 7905, + id: null, +}; + +Object.freeze(sampleWithNewData); +Object.freeze(sampleWithRequiredData); +Object.freeze(sampleWithPartialData); +Object.freeze(sampleWithFullData); diff --git a/src/main/webapp/app/home/home.component.html b/src/main/webapp/app/home/home.component.html new file mode 100644 index 00000000..2a9ed8d7 --- /dev/null +++ b/src/main/webapp/app/home/home.component.html @@ -0,0 +1,78 @@ +
+
+ +
+ +
+

Welcome, Java Hipster! (Twenty One Points)

+ +

This is your homepage

+ +
+
+ You are logged in as user "{{ account.login }}". +
+ +
+ If you want to + sign in, you can try the default accounts:
- Administrator (login="admin" and password="admin")
- User (login="user" and + password="user").
+
+ +
+ You don't have an account yet?  + Register a new account +
+
+ +

If you have any question on JHipster:

+ + + +

+ If you like JHipster, don't forget to give us a star on + GitHub! +

+
+
diff --git a/src/main/webapp/app/home/home.component.scss b/src/main/webapp/app/home/home.component.scss new file mode 100644 index 00000000..c731f12a --- /dev/null +++ b/src/main/webapp/app/home/home.component.scss @@ -0,0 +1,23 @@ +/* ========================================================================== +Main page styles +========================================================================== */ + +.hipster { + display: inline-block; + width: 347px; + height: 497px; + background: url('../../content/images/jhipster_family_member_0.svg') no-repeat center top; + background-size: contain; +} + +/* wait autoprefixer update to allow simple generation of high pixel density media query */ +@media only screen and (-webkit-min-device-pixel-ratio: 2), + only screen and (-moz-min-device-pixel-ratio: 2), + only screen and (-o-min-device-pixel-ratio: 2/1), + only screen and (min-resolution: 192dpi), + only screen and (min-resolution: 2dppx) { + .hipster { + background: url('../../content/images/jhipster_family_member_0.svg') no-repeat center top; + background-size: contain; + } +} diff --git a/src/main/webapp/app/home/home.component.spec.ts b/src/main/webapp/app/home/home.component.spec.ts new file mode 100644 index 00000000..41d7991a --- /dev/null +++ b/src/main/webapp/app/home/home.component.spec.ts @@ -0,0 +1,112 @@ +jest.mock('app/core/auth/account.service'); + +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { Router } from '@angular/router'; +import { RouterTestingModule } from '@angular/router/testing'; +import { of, Subject } from 'rxjs'; + +import { AccountService } from 'app/core/auth/account.service'; +import { Account } from 'app/core/auth/account.model'; + +import { HomeComponent } from './home.component'; + +describe('Home Component', () => { + let comp: HomeComponent; + let fixture: ComponentFixture; + let mockAccountService: AccountService; + let mockRouter: Router; + const account: Account = { + activated: true, + authorities: [], + email: '', + firstName: null, + langKey: '', + lastName: null, + login: 'login', + imageUrl: null, + }; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [RouterTestingModule.withRoutes([])], + declarations: [HomeComponent], + providers: [AccountService], + }) + .overrideTemplate(HomeComponent, '') + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(HomeComponent); + comp = fixture.componentInstance; + mockAccountService = TestBed.inject(AccountService); + mockAccountService.identity = jest.fn(() => of(null)); + mockAccountService.getAuthenticationState = jest.fn(() => of(null)); + + mockRouter = TestBed.inject(Router); + jest.spyOn(mockRouter, 'navigate').mockImplementation(() => Promise.resolve(true)); + }); + + describe('ngOnInit', () => { + it('Should synchronize account variable with current account', () => { + // GIVEN + const authenticationState = new Subject(); + mockAccountService.getAuthenticationState = jest.fn(() => authenticationState.asObservable()); + + // WHEN + comp.ngOnInit(); + + // THEN + expect(comp.account).toBeNull(); + + // WHEN + authenticationState.next(account); + + // THEN + expect(comp.account).toEqual(account); + + // WHEN + authenticationState.next(null); + + // THEN + expect(comp.account).toBeNull(); + }); + }); + + describe('login', () => { + it('Should navigate to /login on login', () => { + // WHEN + comp.login(); + + // THEN + expect(mockRouter.navigate).toHaveBeenCalledWith(['/login']); + }); + }); + + describe('ngOnDestroy', () => { + it('Should destroy authentication state subscription on component destroy', () => { + // GIVEN + const authenticationState = new Subject(); + mockAccountService.getAuthenticationState = jest.fn(() => authenticationState.asObservable()); + + // WHEN + comp.ngOnInit(); + + // THEN + expect(comp.account).toBeNull(); + + // WHEN + authenticationState.next(account); + + // THEN + expect(comp.account).toEqual(account); + + // WHEN + comp.ngOnDestroy(); + authenticationState.next(null); + + // THEN + expect(comp.account).toEqual(account); + }); + }); +}); diff --git a/src/main/webapp/app/home/home.component.ts b/src/main/webapp/app/home/home.component.ts new file mode 100644 index 00000000..90102276 --- /dev/null +++ b/src/main/webapp/app/home/home.component.ts @@ -0,0 +1,36 @@ +import { Component, OnInit, OnDestroy } from '@angular/core'; +import { Router } from '@angular/router'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; + +import { AccountService } from 'app/core/auth/account.service'; +import { Account } from 'app/core/auth/account.model'; + +@Component({ + selector: 'jhi-home', + templateUrl: './home.component.html', + styleUrls: ['./home.component.scss'], +}) +export class HomeComponent implements OnInit, OnDestroy { + account: Account | null = null; + + private readonly destroy$ = new Subject(); + + constructor(private accountService: AccountService, private router: Router) {} + + ngOnInit(): void { + this.accountService + .getAuthenticationState() + .pipe(takeUntil(this.destroy$)) + .subscribe(account => (this.account = account)); + } + + login(): void { + this.router.navigate(['/login']); + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } +} diff --git a/src/main/webapp/app/home/home.module.ts b/src/main/webapp/app/home/home.module.ts new file mode 100644 index 00000000..758f5c48 --- /dev/null +++ b/src/main/webapp/app/home/home.module.ts @@ -0,0 +1,12 @@ +import { NgModule } from '@angular/core'; +import { RouterModule } from '@angular/router'; + +import { SharedModule } from 'app/shared/shared.module'; +import { HOME_ROUTE } from './home.route'; +import { HomeComponent } from './home.component'; + +@NgModule({ + imports: [SharedModule, RouterModule.forChild([HOME_ROUTE])], + declarations: [HomeComponent], +}) +export class HomeModule {} diff --git a/src/main/webapp/app/home/home.route.ts b/src/main/webapp/app/home/home.route.ts new file mode 100644 index 00000000..213002d1 --- /dev/null +++ b/src/main/webapp/app/home/home.route.ts @@ -0,0 +1,11 @@ +import { Route } from '@angular/router'; + +import { HomeComponent } from './home.component'; + +export const HOME_ROUTE: Route = { + path: '', + component: HomeComponent, + data: { + pageTitle: 'home.title', + }, +}; diff --git a/src/main/webapp/app/layouts/error/error.component.html b/src/main/webapp/app/layouts/error/error.component.html new file mode 100644 index 00000000..c1b52a55 --- /dev/null +++ b/src/main/webapp/app/layouts/error/error.component.html @@ -0,0 +1,15 @@ +
+
+
+ +
+ +
+

Error page!

+ +
+
{{ errorMessage }}
+
+
+
+
diff --git a/src/main/webapp/app/layouts/error/error.component.ts b/src/main/webapp/app/layouts/error/error.component.ts new file mode 100644 index 00000000..86ba9782 --- /dev/null +++ b/src/main/webapp/app/layouts/error/error.component.ts @@ -0,0 +1,41 @@ +import { Component, OnInit, OnDestroy } from '@angular/core'; +import { ActivatedRoute } from '@angular/router'; +import { Subscription } from 'rxjs'; +import { TranslateService } from '@ngx-translate/core'; + +@Component({ + selector: 'jhi-error', + templateUrl: './error.component.html', +}) +export class ErrorComponent implements OnInit, OnDestroy { + errorMessage?: string; + errorKey?: string; + langChangeSubscription?: Subscription; + + constructor(private translateService: TranslateService, private route: ActivatedRoute) {} + + ngOnInit(): void { + this.route.data.subscribe(routeData => { + if (routeData.errorMessage) { + this.errorKey = routeData.errorMessage; + this.getErrorMessageTranslation(); + this.langChangeSubscription = this.translateService.onLangChange.subscribe(() => this.getErrorMessageTranslation()); + } + }); + } + + ngOnDestroy(): void { + if (this.langChangeSubscription) { + this.langChangeSubscription.unsubscribe(); + } + } + + private getErrorMessageTranslation(): void { + this.errorMessage = ''; + if (this.errorKey) { + this.translateService.get(this.errorKey).subscribe(translatedErrorMessage => { + this.errorMessage = translatedErrorMessage; + }); + } + } +} diff --git a/src/main/webapp/app/layouts/error/error.route.ts b/src/main/webapp/app/layouts/error/error.route.ts new file mode 100644 index 00000000..a8574019 --- /dev/null +++ b/src/main/webapp/app/layouts/error/error.route.ts @@ -0,0 +1,33 @@ +import { Routes } from '@angular/router'; + +import { ErrorComponent } from './error.component'; + +export const errorRoute: Routes = [ + { + path: 'error', + component: ErrorComponent, + data: { + pageTitle: 'error.title', + }, + }, + { + path: 'accessdenied', + component: ErrorComponent, + data: { + pageTitle: 'error.title', + errorMessage: 'error.http.403', + }, + }, + { + path: '404', + component: ErrorComponent, + data: { + pageTitle: 'error.title', + errorMessage: 'error.http.404', + }, + }, + { + path: '**', + redirectTo: '/404', + }, +]; diff --git a/src/main/webapp/app/layouts/footer/footer.component.html b/src/main/webapp/app/layouts/footer/footer.component.html new file mode 100644 index 00000000..30aca31d --- /dev/null +++ b/src/main/webapp/app/layouts/footer/footer.component.html @@ -0,0 +1,3 @@ + diff --git a/src/main/webapp/app/layouts/footer/footer.component.ts b/src/main/webapp/app/layouts/footer/footer.component.ts new file mode 100644 index 00000000..7c640ec8 --- /dev/null +++ b/src/main/webapp/app/layouts/footer/footer.component.ts @@ -0,0 +1,7 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'jhi-footer', + templateUrl: './footer.component.html', +}) +export class FooterComponent {} diff --git a/src/main/webapp/app/layouts/main/main.component.html b/src/main/webapp/app/layouts/main/main.component.html new file mode 100644 index 00000000..3ac9be94 --- /dev/null +++ b/src/main/webapp/app/layouts/main/main.component.html @@ -0,0 +1,13 @@ + + +
+ +
+ +
+
+ +
+ + +
diff --git a/src/main/webapp/app/layouts/main/main.component.spec.ts b/src/main/webapp/app/layouts/main/main.component.spec.ts new file mode 100644 index 00000000..4e137b5c --- /dev/null +++ b/src/main/webapp/app/layouts/main/main.component.spec.ts @@ -0,0 +1,196 @@ +jest.mock('app/core/auth/account.service'); + +import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing'; +import { Router, RouterEvent, NavigationEnd, NavigationStart } from '@angular/router'; +import { Title } from '@angular/platform-browser'; +import { Subject, of } from 'rxjs'; +import { TranslateModule, TranslateService, LangChangeEvent } from '@ngx-translate/core'; + +import { AccountService } from 'app/core/auth/account.service'; + +import { MainComponent } from './main.component'; + +describe('MainComponent', () => { + let comp: MainComponent; + let fixture: ComponentFixture; + let titleService: Title; + let translateService: TranslateService; + let mockAccountService: AccountService; + const routerEventsSubject = new Subject(); + const routerState: any = { snapshot: { root: { data: {} } } }; + class MockRouter { + events = routerEventsSubject; + routerState = routerState; + } + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot()], + declarations: [MainComponent], + providers: [ + Title, + AccountService, + { + provide: Router, + useClass: MockRouter, + }, + ], + }) + .overrideTemplate(MainComponent, '') + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(MainComponent); + comp = fixture.componentInstance; + titleService = TestBed.inject(Title); + translateService = TestBed.inject(TranslateService); + mockAccountService = TestBed.inject(AccountService); + mockAccountService.identity = jest.fn(() => of(null)); + mockAccountService.getAuthenticationState = jest.fn(() => of(null)); + }); + + describe('page title', () => { + const defaultPageTitle = 'global.title'; + const parentRoutePageTitle = 'parentTitle'; + const childRoutePageTitle = 'childTitle'; + const navigationEnd = new NavigationEnd(1, '', ''); + const navigationStart = new NavigationStart(1, ''); + const langChangeEvent: LangChangeEvent = { lang: 'en', translations: null }; + + beforeEach(() => { + routerState.snapshot.root = { data: {} }; + jest.spyOn(translateService, 'get').mockImplementation((key: string | string[]) => of(`${key as string} translated`)); + translateService.currentLang = 'en'; + jest.spyOn(titleService, 'setTitle'); + comp.ngOnInit(); + }); + + describe('navigation end', () => { + it('should set page title to default title if pageTitle is missing on routes', () => { + // WHEN + routerEventsSubject.next(navigationEnd); + + // THEN + expect(translateService.get).toHaveBeenCalledWith(defaultPageTitle); + expect(titleService.setTitle).toHaveBeenCalledWith(defaultPageTitle + ' translated'); + }); + + it('should set page title to root route pageTitle if there is no child routes', () => { + // GIVEN + routerState.snapshot.root.data = { pageTitle: parentRoutePageTitle }; + + // WHEN + routerEventsSubject.next(navigationEnd); + + // THEN + expect(translateService.get).toHaveBeenCalledWith(parentRoutePageTitle); + expect(titleService.setTitle).toHaveBeenCalledWith(parentRoutePageTitle + ' translated'); + }); + + it('should set page title to child route pageTitle if child routes exist and pageTitle is set for child route', () => { + // GIVEN + routerState.snapshot.root.data = { pageTitle: parentRoutePageTitle }; + routerState.snapshot.root.firstChild = { data: { pageTitle: childRoutePageTitle } }; + + // WHEN + routerEventsSubject.next(navigationEnd); + + // THEN + expect(translateService.get).toHaveBeenCalledWith(childRoutePageTitle); + expect(titleService.setTitle).toHaveBeenCalledWith(childRoutePageTitle + ' translated'); + }); + + it('should set page title to parent route pageTitle if child routes exists but pageTitle is not set for child route data', () => { + // GIVEN + routerState.snapshot.root.data = { pageTitle: parentRoutePageTitle }; + routerState.snapshot.root.firstChild = { data: {} }; + + // WHEN + routerEventsSubject.next(navigationEnd); + + // THEN + expect(translateService.get).toHaveBeenCalledWith(parentRoutePageTitle); + expect(titleService.setTitle).toHaveBeenCalledWith(parentRoutePageTitle + ' translated'); + }); + }); + + describe('navigation start', () => { + it('should not set page title on navigation start', () => { + // WHEN + routerEventsSubject.next(navigationStart); + + // THEN + expect(titleService.setTitle).not.toHaveBeenCalled(); + }); + }); + + describe('language change', () => { + it('should set page title to default title if pageTitle is missing on routes', () => { + // WHEN + translateService.onLangChange.emit(langChangeEvent); + + // THEN + expect(translateService.get).toHaveBeenCalledWith(defaultPageTitle); + expect(titleService.setTitle).toHaveBeenCalledWith(defaultPageTitle + ' translated'); + }); + + it('should set page title to root route pageTitle if there is no child routes', () => { + // GIVEN + routerState.snapshot.root.data = { pageTitle: parentRoutePageTitle }; + + // WHEN + translateService.onLangChange.emit(langChangeEvent); + + // THEN + expect(translateService.get).toHaveBeenCalledWith(parentRoutePageTitle); + expect(titleService.setTitle).toHaveBeenCalledWith(parentRoutePageTitle + ' translated'); + }); + + it('should set page title to child route pageTitle if child routes exist and pageTitle is set for child route', () => { + // GIVEN + routerState.snapshot.root.data = { pageTitle: parentRoutePageTitle }; + routerState.snapshot.root.firstChild = { data: { pageTitle: childRoutePageTitle } }; + + // WHEN + translateService.onLangChange.emit(langChangeEvent); + + // THEN + expect(translateService.get).toHaveBeenCalledWith(childRoutePageTitle); + expect(titleService.setTitle).toHaveBeenCalledWith(childRoutePageTitle + ' translated'); + }); + + it('should set page title to parent route pageTitle if child routes exists but pageTitle is not set for child route data', () => { + // GIVEN + routerState.snapshot.root.data = { pageTitle: parentRoutePageTitle }; + routerState.snapshot.root.firstChild = { data: {} }; + + // WHEN + translateService.onLangChange.emit(langChangeEvent); + + // THEN + expect(translateService.get).toHaveBeenCalledWith(parentRoutePageTitle); + expect(titleService.setTitle).toHaveBeenCalledWith(parentRoutePageTitle + ' translated'); + }); + }); + }); + + describe('page language attribute', () => { + it('should change page language attribute on language change', () => { + // GIVEN + comp.ngOnInit(); + + // WHEN + translateService.onLangChange.emit({ lang: 'lang1', translations: null }); + + // THEN + expect(document.querySelector('html')?.getAttribute('lang')).toEqual('lang1'); + + // WHEN + translateService.onLangChange.emit({ lang: 'lang2', translations: null }); + + // THEN + expect(document.querySelector('html')?.getAttribute('lang')).toEqual('lang2'); + }); + }); +}); diff --git a/src/main/webapp/app/layouts/main/main.component.ts b/src/main/webapp/app/layouts/main/main.component.ts new file mode 100644 index 00000000..2079bc99 --- /dev/null +++ b/src/main/webapp/app/layouts/main/main.component.ts @@ -0,0 +1,58 @@ +import { Component, OnInit, RendererFactory2, Renderer2 } from '@angular/core'; +import { Title } from '@angular/platform-browser'; +import { Router, ActivatedRouteSnapshot, NavigationEnd } from '@angular/router'; +import { TranslateService, LangChangeEvent } from '@ngx-translate/core'; +import dayjs from 'dayjs/esm'; + +import { AccountService } from 'app/core/auth/account.service'; + +@Component({ + selector: 'jhi-main', + templateUrl: './main.component.html', +}) +export class MainComponent implements OnInit { + private renderer: Renderer2; + + constructor( + private accountService: AccountService, + private titleService: Title, + private router: Router, + private translateService: TranslateService, + rootRenderer: RendererFactory2 + ) { + this.renderer = rootRenderer.createRenderer(document.querySelector('html'), null); + } + + ngOnInit(): void { + // try to log in automatically + this.accountService.identity().subscribe(); + + this.router.events.subscribe(event => { + if (event instanceof NavigationEnd) { + this.updateTitle(); + } + }); + + this.translateService.onLangChange.subscribe((langChangeEvent: LangChangeEvent) => { + this.updateTitle(); + dayjs.locale(langChangeEvent.lang); + this.renderer.setAttribute(document.querySelector('html'), 'lang', langChangeEvent.lang); + }); + } + + private getPageTitle(routeSnapshot: ActivatedRouteSnapshot): string { + const title: string = routeSnapshot.data['pageTitle'] ?? ''; + if (routeSnapshot.firstChild) { + return this.getPageTitle(routeSnapshot.firstChild) || title; + } + return title; + } + + private updateTitle(): void { + let pageTitle = this.getPageTitle(this.router.routerState.snapshot.root); + if (!pageTitle) { + pageTitle = 'global.title'; + } + this.translateService.get(pageTitle).subscribe(title => this.titleService.setTitle(title)); + } +} diff --git a/src/main/webapp/app/layouts/navbar/active-menu.directive.ts b/src/main/webapp/app/layouts/navbar/active-menu.directive.ts new file mode 100644 index 00000000..6150841e --- /dev/null +++ b/src/main/webapp/app/layouts/navbar/active-menu.directive.ts @@ -0,0 +1,27 @@ +import { Directive, OnInit, ElementRef, Renderer2, Input } from '@angular/core'; +import { TranslateService, LangChangeEvent } from '@ngx-translate/core'; + +@Directive({ + selector: '[jhiActiveMenu]', +}) +export class ActiveMenuDirective implements OnInit { + @Input() jhiActiveMenu?: string; + + constructor(private el: ElementRef, private renderer: Renderer2, private translateService: TranslateService) {} + + ngOnInit(): void { + this.translateService.onLangChange.subscribe((event: LangChangeEvent) => { + this.updateActiveFlag(event.lang); + }); + + this.updateActiveFlag(this.translateService.currentLang); + } + + updateActiveFlag(selectedLanguage: string): void { + if (this.jhiActiveMenu === selectedLanguage) { + this.renderer.addClass(this.el.nativeElement, 'active'); + } else { + this.renderer.removeClass(this.el.nativeElement, 'active'); + } + } +} diff --git a/src/main/webapp/app/layouts/navbar/navbar.component.html b/src/main/webapp/app/layouts/navbar/navbar.component.html new file mode 100644 index 00000000..118b8057 --- /dev/null +++ b/src/main/webapp/app/layouts/navbar/navbar.component.html @@ -0,0 +1,252 @@ + diff --git a/src/main/webapp/app/layouts/navbar/navbar.component.scss b/src/main/webapp/app/layouts/navbar/navbar.component.scss new file mode 100644 index 00000000..8ff0a053 --- /dev/null +++ b/src/main/webapp/app/layouts/navbar/navbar.component.scss @@ -0,0 +1,36 @@ +@import '~bootstrap/scss/functions'; +@import '~bootstrap/scss/variables'; + +/* ========================================================================== +Navbar +========================================================================== */ + +.navbar-version { + font-size: 0.65em; + color: $navbar-dark-color; +} + +.profile-image { + height: 1.75em; + width: 1.75em; +} + +.navbar { + padding: 0.2rem 1rem; + + a.nav-link { + font-weight: 400; + } +} + +/* ========================================================================== +Logo styles +========================================================================== */ +.logo-img { + height: 45px; + width: 45px; + display: inline-block; + vertical-align: middle; + background: url('../../../content/images/logo-jhipster.png') no-repeat center center; + background-size: contain; +} diff --git a/src/main/webapp/app/layouts/navbar/navbar.component.spec.ts b/src/main/webapp/app/layouts/navbar/navbar.component.spec.ts new file mode 100644 index 00000000..51885374 --- /dev/null +++ b/src/main/webapp/app/layouts/navbar/navbar.component.spec.ts @@ -0,0 +1,98 @@ +jest.mock('app/login/login.service'); + +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { RouterTestingModule } from '@angular/router/testing'; +import { of } from 'rxjs'; +import { NgxWebstorageModule } from 'ngx-webstorage'; +import { TranslateModule } from '@ngx-translate/core'; + +import { ProfileInfo } from 'app/layouts/profiles/profile-info.model'; +import { Account } from 'app/core/auth/account.model'; +import { AccountService } from 'app/core/auth/account.service'; +import { ProfileService } from 'app/layouts/profiles/profile.service'; +import { LoginService } from 'app/login/login.service'; + +import { NavbarComponent } from './navbar.component'; + +describe('Navbar Component', () => { + let comp: NavbarComponent; + let fixture: ComponentFixture; + let accountService: AccountService; + let profileService: ProfileService; + const account: Account = { + activated: true, + authorities: [], + email: '', + firstName: 'John', + langKey: '', + lastName: 'Doe', + login: 'john.doe', + imageUrl: '', + }; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), NgxWebstorageModule.forRoot()], + declarations: [NavbarComponent], + providers: [LoginService], + }) + .overrideTemplate(NavbarComponent, '') + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(NavbarComponent); + comp = fixture.componentInstance; + accountService = TestBed.inject(AccountService); + profileService = TestBed.inject(ProfileService); + }); + + it('Should call profileService.getProfileInfo on init', () => { + // GIVEN + jest.spyOn(profileService, 'getProfileInfo').mockReturnValue(of(new ProfileInfo())); + + // WHEN + comp.ngOnInit(); + + // THEN + expect(profileService.getProfileInfo).toHaveBeenCalled(); + }); + + it('Should hold current authenticated user in variable account', () => { + // WHEN + comp.ngOnInit(); + + // THEN + expect(comp.account).toBeNull(); + + // WHEN + accountService.authenticate(account); + + // THEN + expect(comp.account).toEqual(account); + + // WHEN + accountService.authenticate(null); + + // THEN + expect(comp.account).toBeNull(); + }); + + it('Should hold current authenticated user in variable account if user is authenticated before page load', () => { + // GIVEN + accountService.authenticate(account); + + // WHEN + comp.ngOnInit(); + + // THEN + expect(comp.account).toEqual(account); + + // WHEN + accountService.authenticate(null); + + // THEN + expect(comp.account).toBeNull(); + }); +}); diff --git a/src/main/webapp/app/layouts/navbar/navbar.component.ts b/src/main/webapp/app/layouts/navbar/navbar.component.ts new file mode 100644 index 00000000..c9c3f6dd --- /dev/null +++ b/src/main/webapp/app/layouts/navbar/navbar.component.ts @@ -0,0 +1,75 @@ +import { Component, OnInit } from '@angular/core'; +import { Router } from '@angular/router'; +import { TranslateService } from '@ngx-translate/core'; +import { SessionStorageService } from 'ngx-webstorage'; + +import { VERSION } from 'app/app.constants'; +import { LANGUAGES } from 'app/config/language.constants'; +import { Account } from 'app/core/auth/account.model'; +import { AccountService } from 'app/core/auth/account.service'; +import { LoginService } from 'app/login/login.service'; +import { ProfileService } from 'app/layouts/profiles/profile.service'; +import { EntityNavbarItems } from 'app/entities/entity-navbar-items'; + +@Component({ + selector: 'jhi-navbar', + templateUrl: './navbar.component.html', + styleUrls: ['./navbar.component.scss'], +}) +export class NavbarComponent implements OnInit { + inProduction?: boolean; + isNavbarCollapsed = true; + languages = LANGUAGES; + openAPIEnabled?: boolean; + version = ''; + account: Account | null = null; + entitiesNavbarItems: any[] = []; + + constructor( + private loginService: LoginService, + private translateService: TranslateService, + private sessionStorageService: SessionStorageService, + private accountService: AccountService, + private profileService: ProfileService, + private router: Router + ) { + if (VERSION) { + this.version = VERSION.toLowerCase().startsWith('v') ? VERSION : `v${VERSION}`; + } + } + + ngOnInit(): void { + this.entitiesNavbarItems = EntityNavbarItems; + this.profileService.getProfileInfo().subscribe(profileInfo => { + this.inProduction = profileInfo.inProduction; + this.openAPIEnabled = profileInfo.openAPIEnabled; + }); + + this.accountService.getAuthenticationState().subscribe(account => { + this.account = account; + }); + } + + changeLanguage(languageKey: string): void { + this.sessionStorageService.store('locale', languageKey); + this.translateService.use(languageKey); + } + + collapseNavbar(): void { + this.isNavbarCollapsed = true; + } + + login(): void { + this.router.navigate(['/login']); + } + + logout(): void { + this.collapseNavbar(); + this.loginService.logout(); + this.router.navigate(['']); + } + + toggleNavbar(): void { + this.isNavbarCollapsed = !this.isNavbarCollapsed; + } +} diff --git a/src/main/webapp/app/layouts/navbar/navbar.route.ts b/src/main/webapp/app/layouts/navbar/navbar.route.ts new file mode 100644 index 00000000..82c591f3 --- /dev/null +++ b/src/main/webapp/app/layouts/navbar/navbar.route.ts @@ -0,0 +1,9 @@ +import { Route } from '@angular/router'; + +import { NavbarComponent } from './navbar.component'; + +export const navbarRoute: Route = { + path: '', + component: NavbarComponent, + outlet: 'navbar', +}; diff --git a/src/main/webapp/app/layouts/profiles/page-ribbon.component.scss b/src/main/webapp/app/layouts/profiles/page-ribbon.component.scss new file mode 100644 index 00000000..88b06022 --- /dev/null +++ b/src/main/webapp/app/layouts/profiles/page-ribbon.component.scss @@ -0,0 +1,25 @@ +/* ========================================================================== +Developement Ribbon +========================================================================== */ +.ribbon { + background-color: rgba(170, 0, 0, 0.5); + overflow: hidden; + position: absolute; + top: 40px; + white-space: nowrap; + width: 15em; + z-index: 9999; + pointer-events: none; + opacity: 0.75; + a { + color: #fff; + display: block; + font-weight: 400; + margin: 1px 0; + padding: 10px 50px; + text-align: center; + text-decoration: none; + text-shadow: 0 0 5px #444; + pointer-events: none; + } +} diff --git a/src/main/webapp/app/layouts/profiles/page-ribbon.component.spec.ts b/src/main/webapp/app/layouts/profiles/page-ribbon.component.spec.ts new file mode 100644 index 00000000..58d99d2c --- /dev/null +++ b/src/main/webapp/app/layouts/profiles/page-ribbon.component.spec.ts @@ -0,0 +1,40 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { of } from 'rxjs'; + +import { ProfileInfo } from 'app/layouts/profiles/profile-info.model'; +import { ProfileService } from 'app/layouts/profiles/profile.service'; + +import { PageRibbonComponent } from './page-ribbon.component'; + +describe('Page Ribbon Component', () => { + let comp: PageRibbonComponent; + let fixture: ComponentFixture; + let profileService: ProfileService; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + declarations: [PageRibbonComponent], + }) + .overrideTemplate(PageRibbonComponent, '') + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(PageRibbonComponent); + comp = fixture.componentInstance; + profileService = TestBed.inject(ProfileService); + }); + + it('Should call profileService.getProfileInfo on init', () => { + // GIVEN + jest.spyOn(profileService, 'getProfileInfo').mockReturnValue(of(new ProfileInfo())); + + // WHEN + comp.ngOnInit(); + + // THEN + expect(profileService.getProfileInfo).toHaveBeenCalled(); + }); +}); diff --git a/src/main/webapp/app/layouts/profiles/page-ribbon.component.ts b/src/main/webapp/app/layouts/profiles/page-ribbon.component.ts new file mode 100644 index 00000000..2f0c9de4 --- /dev/null +++ b/src/main/webapp/app/layouts/profiles/page-ribbon.component.ts @@ -0,0 +1,24 @@ +import { Component, OnInit } from '@angular/core'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; + +import { ProfileService } from './profile.service'; + +@Component({ + selector: 'jhi-page-ribbon', + template: ` + + `, + styleUrls: ['./page-ribbon.component.scss'], +}) +export class PageRibbonComponent implements OnInit { + ribbonEnv$?: Observable; + + constructor(private profileService: ProfileService) {} + + ngOnInit(): void { + this.ribbonEnv$ = this.profileService.getProfileInfo().pipe(map(profileInfo => profileInfo.ribbonEnv)); + } +} diff --git a/src/main/webapp/app/layouts/profiles/profile-info.model.ts b/src/main/webapp/app/layouts/profiles/profile-info.model.ts new file mode 100644 index 00000000..8c769c74 --- /dev/null +++ b/src/main/webapp/app/layouts/profiles/profile-info.model.ts @@ -0,0 +1,15 @@ +export interface InfoResponse { + 'display-ribbon-on-profiles'?: string; + git?: any; + build?: any; + activeProfiles?: string[]; +} + +export class ProfileInfo { + constructor( + public activeProfiles?: string[], + public ribbonEnv?: string, + public inProduction?: boolean, + public openAPIEnabled?: boolean + ) {} +} diff --git a/src/main/webapp/app/layouts/profiles/profile.service.ts b/src/main/webapp/app/layouts/profiles/profile.service.ts new file mode 100644 index 00000000..b379d3cb --- /dev/null +++ b/src/main/webapp/app/layouts/profiles/profile.service.ts @@ -0,0 +1,41 @@ +import { Injectable } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { map, shareReplay } from 'rxjs/operators'; +import { Observable } from 'rxjs'; + +import { ApplicationConfigService } from 'app/core/config/application-config.service'; +import { ProfileInfo, InfoResponse } from './profile-info.model'; + +@Injectable({ providedIn: 'root' }) +export class ProfileService { + private infoUrl = this.applicationConfigService.getEndpointFor('management/info'); + private profileInfo$?: Observable; + + constructor(private http: HttpClient, private applicationConfigService: ApplicationConfigService) {} + + getProfileInfo(): Observable { + if (this.profileInfo$) { + return this.profileInfo$; + } + + this.profileInfo$ = this.http.get(this.infoUrl).pipe( + map((response: InfoResponse) => { + const profileInfo: ProfileInfo = { + activeProfiles: response.activeProfiles, + inProduction: response.activeProfiles?.includes('prod'), + openAPIEnabled: response.activeProfiles?.includes('api-docs'), + }; + if (response.activeProfiles && response['display-ribbon-on-profiles']) { + const displayRibbonOnProfiles = response['display-ribbon-on-profiles'].split(','); + const ribbonProfiles = displayRibbonOnProfiles.filter(profile => response.activeProfiles?.includes(profile)); + if (ribbonProfiles.length > 0) { + profileInfo.ribbonEnv = ribbonProfiles[0]; + } + } + return profileInfo; + }), + shareReplay() + ); + return this.profileInfo$; + } +} diff --git a/src/main/webapp/app/login/login.component.html b/src/main/webapp/app/login/login.component.html new file mode 100644 index 00000000..f4e6b31b --- /dev/null +++ b/src/main/webapp/app/login/login.component.html @@ -0,0 +1,57 @@ +
+
+
+

Sign in

+
+ Failed to sign in! Please check your credentials and try again. +
+
+
+ + +
+ +
+ + +
+ +
+ +
+ + +
+ + +
+ You don't have an account yet? + Register a new account +
+
+
+
diff --git a/src/main/webapp/app/login/login.component.spec.ts b/src/main/webapp/app/login/login.component.spec.ts new file mode 100644 index 00000000..8bc4d99a --- /dev/null +++ b/src/main/webapp/app/login/login.component.spec.ts @@ -0,0 +1,153 @@ +jest.mock('app/core/auth/account.service'); +jest.mock('app/login/login.service'); + +import { ElementRef } from '@angular/core'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { FormBuilder } from '@angular/forms'; +import { Router, Navigation } from '@angular/router'; +import { RouterTestingModule } from '@angular/router/testing'; +import { of, throwError } from 'rxjs'; + +import { AccountService } from 'app/core/auth/account.service'; + +import { LoginService } from './login.service'; +import { LoginComponent } from './login.component'; + +describe('LoginComponent', () => { + let comp: LoginComponent; + let fixture: ComponentFixture; + let mockRouter: Router; + let mockAccountService: AccountService; + let mockLoginService: LoginService; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [RouterTestingModule.withRoutes([])], + declarations: [LoginComponent], + providers: [ + FormBuilder, + AccountService, + { + provide: LoginService, + useValue: { + login: jest.fn(() => of({})), + }, + }, + ], + }) + .overrideTemplate(LoginComponent, '') + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(LoginComponent); + comp = fixture.componentInstance; + mockRouter = TestBed.inject(Router); + jest.spyOn(mockRouter, 'navigate').mockImplementation(() => Promise.resolve(true)); + mockLoginService = TestBed.inject(LoginService); + mockAccountService = TestBed.inject(AccountService); + }); + + describe('ngOnInit', () => { + it('Should call accountService.identity on Init', () => { + // GIVEN + mockAccountService.identity = jest.fn(() => of(null)); + mockAccountService.getAuthenticationState = jest.fn(() => of(null)); + + // WHEN + comp.ngOnInit(); + + // THEN + expect(mockAccountService.identity).toHaveBeenCalled(); + }); + + it('Should call accountService.isAuthenticated on Init', () => { + // GIVEN + mockAccountService.identity = jest.fn(() => of(null)); + + // WHEN + comp.ngOnInit(); + + // THEN + expect(mockAccountService.isAuthenticated).toHaveBeenCalled(); + }); + + it('should navigate to home page on Init if authenticated=true', () => { + // GIVEN + mockAccountService.identity = jest.fn(() => of(null)); + mockAccountService.getAuthenticationState = jest.fn(() => of(null)); + mockAccountService.isAuthenticated = () => true; + + // WHEN + comp.ngOnInit(); + + // THEN + expect(mockRouter.navigate).toHaveBeenCalledWith(['']); + }); + }); + + describe('ngAfterViewInit', () => { + it('shoult set focus to username input after the view has been initialized', () => { + // GIVEN + const node = { + focus: jest.fn(), + }; + comp.username = new ElementRef(node); + + // WHEN + comp.ngAfterViewInit(); + + // THEN + expect(node.focus).toHaveBeenCalled(); + }); + }); + + describe('login', () => { + it('should authenticate the user and navigate to home page', () => { + // GIVEN + const credentials = { + username: 'admin', + password: 'admin', + rememberMe: true, + }; + + comp.loginForm.patchValue({ + username: 'admin', + password: 'admin', + rememberMe: true, + }); + + // WHEN + comp.login(); + + // THEN + expect(comp.authenticationError).toEqual(false); + expect(mockLoginService.login).toHaveBeenCalledWith(credentials); + expect(mockRouter.navigate).toHaveBeenCalledWith(['']); + }); + + it('should authenticate the user but not navigate to home page if authentication process is already routing to cached url from localstorage', () => { + // GIVEN + jest.spyOn(mockRouter, 'getCurrentNavigation').mockReturnValue({} as Navigation); + + // WHEN + comp.login(); + + // THEN + expect(comp.authenticationError).toEqual(false); + expect(mockRouter.navigate).not.toHaveBeenCalled(); + }); + + it('should stay on login form and show error message on login error', () => { + // GIVEN + mockLoginService.login = jest.fn(() => throwError({})); + + // WHEN + comp.login(); + + // THEN + expect(comp.authenticationError).toEqual(true); + expect(mockRouter.navigate).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/src/main/webapp/app/login/login.component.ts b/src/main/webapp/app/login/login.component.ts new file mode 100644 index 00000000..6b751acc --- /dev/null +++ b/src/main/webapp/app/login/login.component.ts @@ -0,0 +1,51 @@ +import { Component, ViewChild, OnInit, AfterViewInit, ElementRef } from '@angular/core'; +import { FormGroup, FormControl, Validators } from '@angular/forms'; +import { Router } from '@angular/router'; + +import { LoginService } from 'app/login/login.service'; +import { AccountService } from 'app/core/auth/account.service'; + +@Component({ + selector: 'jhi-login', + templateUrl: './login.component.html', +}) +export class LoginComponent implements OnInit, AfterViewInit { + @ViewChild('username', { static: false }) + username!: ElementRef; + + authenticationError = false; + + loginForm = new FormGroup({ + username: new FormControl('', { nonNullable: true, validators: [Validators.required] }), + password: new FormControl('', { nonNullable: true, validators: [Validators.required] }), + rememberMe: new FormControl(false, { nonNullable: true, validators: [Validators.required] }), + }); + + constructor(private accountService: AccountService, private loginService: LoginService, private router: Router) {} + + ngOnInit(): void { + // if already authenticated then navigate to home page + this.accountService.identity().subscribe(() => { + if (this.accountService.isAuthenticated()) { + this.router.navigate(['']); + } + }); + } + + ngAfterViewInit(): void { + this.username.nativeElement.focus(); + } + + login(): void { + this.loginService.login(this.loginForm.getRawValue()).subscribe({ + next: () => { + this.authenticationError = false; + if (!this.router.getCurrentNavigation()) { + // There were no routing during login (eg from navigationToStoredUrl) + this.router.navigate(['']); + } + }, + error: () => (this.authenticationError = true), + }); + } +} diff --git a/src/main/webapp/app/login/login.model.ts b/src/main/webapp/app/login/login.model.ts new file mode 100644 index 00000000..422fce9a --- /dev/null +++ b/src/main/webapp/app/login/login.model.ts @@ -0,0 +1,3 @@ +export class Login { + constructor(public username: string, public password: string, public rememberMe: boolean) {} +} diff --git a/src/main/webapp/app/login/login.module.ts b/src/main/webapp/app/login/login.module.ts new file mode 100644 index 00000000..6dbd1852 --- /dev/null +++ b/src/main/webapp/app/login/login.module.ts @@ -0,0 +1,12 @@ +import { NgModule } from '@angular/core'; +import { RouterModule } from '@angular/router'; + +import { SharedModule } from 'app/shared/shared.module'; +import { LOGIN_ROUTE } from './login.route'; +import { LoginComponent } from './login.component'; + +@NgModule({ + imports: [SharedModule, RouterModule.forChild([LOGIN_ROUTE])], + declarations: [LoginComponent], +}) +export class LoginModule {} diff --git a/src/main/webapp/app/login/login.route.ts b/src/main/webapp/app/login/login.route.ts new file mode 100644 index 00000000..002cc9dc --- /dev/null +++ b/src/main/webapp/app/login/login.route.ts @@ -0,0 +1,11 @@ +import { Route } from '@angular/router'; + +import { LoginComponent } from './login.component'; + +export const LOGIN_ROUTE: Route = { + path: '', + component: LoginComponent, + data: { + pageTitle: 'login.title', + }, +}; diff --git a/src/main/webapp/app/login/login.service.ts b/src/main/webapp/app/login/login.service.ts new file mode 100644 index 00000000..bc97be6e --- /dev/null +++ b/src/main/webapp/app/login/login.service.ts @@ -0,0 +1,21 @@ +import { Injectable } from '@angular/core'; +import { Observable } from 'rxjs'; +import { mergeMap } from 'rxjs/operators'; + +import { Account } from 'app/core/auth/account.model'; +import { AccountService } from 'app/core/auth/account.service'; +import { AuthServerProvider } from 'app/core/auth/auth-jwt.service'; +import { Login } from './login.model'; + +@Injectable({ providedIn: 'root' }) +export class LoginService { + constructor(private accountService: AccountService, private authServerProvider: AuthServerProvider) {} + + login(credentials: Login): Observable { + return this.authServerProvider.login(credentials).pipe(mergeMap(() => this.accountService.identity(true))); + } + + logout(): void { + this.authServerProvider.logout().subscribe({ complete: () => this.accountService.authenticate(null) }); + } +} diff --git a/src/main/webapp/app/shared/alert/alert-error.component.html b/src/main/webapp/app/shared/alert/alert-error.component.html new file mode 100644 index 00000000..76ff881d --- /dev/null +++ b/src/main/webapp/app/shared/alert/alert-error.component.html @@ -0,0 +1,7 @@ + diff --git a/src/main/webapp/app/shared/alert/alert-error.component.spec.ts b/src/main/webapp/app/shared/alert/alert-error.component.spec.ts new file mode 100644 index 00000000..a2a4bbab --- /dev/null +++ b/src/main/webapp/app/shared/alert/alert-error.component.spec.ts @@ -0,0 +1,160 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { HttpErrorResponse, HttpHeaders } from '@angular/common/http'; +import { TranslateModule } from '@ngx-translate/core'; + +import { EventManager } from 'app/core/util/event-manager.service'; +import { Alert, AlertService } from 'app/core/util/alert.service'; + +import { AlertErrorComponent } from './alert-error.component'; + +describe('Alert Error Component', () => { + let comp: AlertErrorComponent; + let fixture: ComponentFixture; + let eventManager: EventManager; + let alertService: AlertService; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot()], + declarations: [AlertErrorComponent], + providers: [EventManager, AlertService], + }) + .overrideTemplate(AlertErrorComponent, '') + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(AlertErrorComponent); + comp = fixture.componentInstance; + eventManager = TestBed.inject(EventManager); + alertService = TestBed.inject(AlertService); + alertService.addAlert = (alert: Alert, alerts?: Alert[]) => { + if (alerts) { + alerts.push(alert); + } + return alert; + }; + }); + + describe('Error Handling', () => { + it('Should display an alert on status 0', () => { + // GIVEN + eventManager.broadcast({ name: 'twentyOnePointsApp.httpError', content: { status: 0 } }); + // THEN + expect(comp.alerts.length).toBe(1); + expect(comp.alerts[0].translationKey).toBe('error.server.not.reachable'); + }); + + it('Should display an alert on status 404', () => { + // GIVEN + eventManager.broadcast({ name: 'twentyOnePointsApp.httpError', content: { status: 404 } }); + // THEN + expect(comp.alerts.length).toBe(1); + expect(comp.alerts[0].translationKey).toBe('error.url.not.found'); + }); + + it('Should display an alert on generic error', () => { + // GIVEN + eventManager.broadcast({ name: 'twentyOnePointsApp.httpError', content: { error: { message: 'Error Message' } } }); + eventManager.broadcast({ name: 'twentyOnePointsApp.httpError', content: { error: 'Second Error Message' } }); + // THEN + expect(comp.alerts.length).toBe(2); + expect(comp.alerts[0].translationKey).toBe('Error Message'); + expect(comp.alerts[1].translationKey).toBe('Second Error Message'); + }); + + it('Should display an alert on status 400 for generic error', () => { + // GIVEN + const response = new HttpErrorResponse({ + url: 'http://localhost:8080/api/foos', + headers: new HttpHeaders(), + status: 400, + statusText: 'Bad Request', + error: { + type: 'https://www.jhipster.tech/problem/constraint-violation', + title: 'Bad Request', + status: 400, + path: '/api/foos', + message: 'error.validation', + }, + }); + eventManager.broadcast({ name: 'twentyOnePointsApp.httpError', content: response }); + // THEN + expect(comp.alerts.length).toBe(1); + expect(comp.alerts[0].translationKey).toBe('error.validation'); + }); + + it('Should display an alert on status 400 for generic error without message', () => { + // GIVEN + const response = new HttpErrorResponse({ + url: 'http://localhost:8080/api/foos', + headers: new HttpHeaders(), + status: 400, + error: 'Bad Request', + }); + eventManager.broadcast({ name: 'twentyOnePointsApp.httpError', content: response }); + // THEN + expect(comp.alerts.length).toBe(1); + expect(comp.alerts[0].translationKey).toBe('Bad Request'); + }); + + it('Should display an alert on status 400 for invalid parameters', () => { + // GIVEN + const response = new HttpErrorResponse({ + url: 'http://localhost:8080/api/foos', + headers: new HttpHeaders(), + status: 400, + statusText: 'Bad Request', + error: { + type: 'https://www.jhipster.tech/problem/constraint-violation', + title: 'Method argument not valid', + status: 400, + path: '/api/foos', + message: 'error.validation', + fieldErrors: [{ objectName: 'foo', field: 'minField', message: 'Min' }], + }, + }); + eventManager.broadcast({ name: 'twentyOnePointsApp.httpError', content: response }); + // THEN + expect(comp.alerts.length).toBe(1); + expect(comp.alerts[0].translationKey).toBe('error.Size'); + }); + + it('Should display an alert on status 400 for error headers', () => { + // GIVEN + const response = new HttpErrorResponse({ + url: 'http://localhost:8080/api/foos', + headers: new HttpHeaders().append('app-error', 'Error Message').append('app-params', 'foo'), + status: 400, + statusText: 'Bad Request', + error: { + status: 400, + message: 'error.validation', + }, + }); + eventManager.broadcast({ name: 'twentyOnePointsApp.httpError', content: response }); + // THEN + expect(comp.alerts.length).toBe(1); + expect(comp.alerts[0].translationKey).toBe('Error Message'); + }); + + it('Should display an alert on status 500 with detail', () => { + // GIVEN + const response = new HttpErrorResponse({ + url: 'http://localhost:8080/api/foos', + headers: new HttpHeaders(), + status: 500, + statusText: 'Internal server error', + error: { + status: 500, + message: 'error.http.500', + detail: 'Detailed error message', + }, + }); + eventManager.broadcast({ name: 'twentyOnePointsApp.httpError', content: response }); + // THEN + expect(comp.alerts.length).toBe(1); + expect(comp.alerts[0].translationKey).toBe('error.http.500'); + }); + }); +}); diff --git a/src/main/webapp/app/shared/alert/alert-error.component.ts b/src/main/webapp/app/shared/alert/alert-error.component.ts new file mode 100644 index 00000000..6b0e53a4 --- /dev/null +++ b/src/main/webapp/app/shared/alert/alert-error.component.ts @@ -0,0 +1,108 @@ +import { Component, OnDestroy } from '@angular/core'; +import { HttpErrorResponse } from '@angular/common/http'; +import { Subscription } from 'rxjs'; +import { TranslateService } from '@ngx-translate/core'; + +import { AlertError } from './alert-error.model'; +import { Alert, AlertService } from 'app/core/util/alert.service'; +import { EventManager, EventWithContent } from 'app/core/util/event-manager.service'; + +@Component({ + selector: 'jhi-alert-error', + templateUrl: './alert-error.component.html', +}) +export class AlertErrorComponent implements OnDestroy { + alerts: Alert[] = []; + errorListener: Subscription; + httpErrorListener: Subscription; + + constructor(private alertService: AlertService, private eventManager: EventManager, translateService: TranslateService) { + this.errorListener = eventManager.subscribe('twentyOnePointsApp.error', (response: EventWithContent | string) => { + const errorResponse = (response as EventWithContent).content; + this.addErrorAlert(errorResponse.message, errorResponse.key, errorResponse.params); + }); + + this.httpErrorListener = eventManager.subscribe('twentyOnePointsApp.httpError', (response: EventWithContent | string) => { + const httpErrorResponse = (response as EventWithContent).content; + switch (httpErrorResponse.status) { + // connection refused, server not reachable + case 0: + this.addErrorAlert('Server not reachable', 'error.server.not.reachable'); + break; + + case 400: { + const arr = httpErrorResponse.headers.keys(); + let errorHeader: string | null = null; + let entityKey: string | null = null; + for (const entry of arr) { + if (entry.toLowerCase().endsWith('app-error')) { + errorHeader = httpErrorResponse.headers.get(entry); + } else if (entry.toLowerCase().endsWith('app-params')) { + entityKey = httpErrorResponse.headers.get(entry); + } + } + if (errorHeader) { + const alertData = entityKey ? { entityName: translateService.instant(`global.menu.entities.${entityKey}`) } : undefined; + this.addErrorAlert(errorHeader, errorHeader, alertData); + } else if (httpErrorResponse.error !== '' && httpErrorResponse.error.fieldErrors) { + const fieldErrors = httpErrorResponse.error.fieldErrors; + for (const fieldError of fieldErrors) { + if (['Min', 'Max', 'DecimalMin', 'DecimalMax'].includes(fieldError.message)) { + fieldError.message = 'Size'; + } + // convert 'something[14].other[4].id' to 'something[].other[].id' so translations can be written to it + const convertedField: string = fieldError.field.replace(/\[\d*\]/g, '[]'); + const fieldName: string = translateService.instant(`twentyOnePointsApp.${fieldError.objectName as string}.${convertedField}`); + this.addErrorAlert(`Error on field "${fieldName}"`, `error.${fieldError.message as string}`, { fieldName }); + } + } else if (httpErrorResponse.error !== '' && httpErrorResponse.error.message) { + this.addErrorAlert( + httpErrorResponse.error.detail ?? httpErrorResponse.error.message, + httpErrorResponse.error.message, + httpErrorResponse.error.params + ); + } else { + this.addErrorAlert(httpErrorResponse.error, httpErrorResponse.error); + } + break; + } + + case 404: + this.addErrorAlert('Not found', 'error.url.not.found'); + break; + + default: + if (httpErrorResponse.error !== '' && httpErrorResponse.error.message) { + this.addErrorAlert( + httpErrorResponse.error.detail ?? httpErrorResponse.error.message, + httpErrorResponse.error.message, + httpErrorResponse.error.params + ); + } else { + this.addErrorAlert(httpErrorResponse.error, httpErrorResponse.error); + } + } + }); + } + + setClasses(alert: Alert): { [key: string]: boolean } { + const classes = { 'jhi-toast': Boolean(alert.toast) }; + if (alert.position) { + return { ...classes, [alert.position]: true }; + } + return classes; + } + + ngOnDestroy(): void { + this.eventManager.destroy(this.errorListener); + this.eventManager.destroy(this.httpErrorListener); + } + + close(alert: Alert): void { + alert.close?.(this.alerts); + } + + private addErrorAlert(message?: string, translationKey?: string, translationParams?: { [key: string]: unknown }): void { + this.alertService.addAlert({ type: 'danger', message, translationKey, translationParams }, this.alerts); + } +} diff --git a/src/main/webapp/app/shared/alert/alert-error.model.ts b/src/main/webapp/app/shared/alert/alert-error.model.ts new file mode 100644 index 00000000..4fca767c --- /dev/null +++ b/src/main/webapp/app/shared/alert/alert-error.model.ts @@ -0,0 +1,3 @@ +export class AlertError { + constructor(public message: string, public key?: string, public params?: { [key: string]: unknown }) {} +} diff --git a/src/main/webapp/app/shared/alert/alert.component.html b/src/main/webapp/app/shared/alert/alert.component.html new file mode 100644 index 00000000..76ff881d --- /dev/null +++ b/src/main/webapp/app/shared/alert/alert.component.html @@ -0,0 +1,7 @@ + diff --git a/src/main/webapp/app/shared/alert/alert.component.spec.ts b/src/main/webapp/app/shared/alert/alert.component.spec.ts new file mode 100644 index 00000000..39952d0a --- /dev/null +++ b/src/main/webapp/app/shared/alert/alert.component.spec.ts @@ -0,0 +1,44 @@ +jest.mock('app/core/util/alert.service'); + +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; + +import { AlertService } from 'app/core/util/alert.service'; + +import { AlertComponent } from './alert.component'; + +describe('Alert Component', () => { + let comp: AlertComponent; + let fixture: ComponentFixture; + let mockAlertService: AlertService; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [AlertComponent], + providers: [AlertService], + }) + .overrideTemplate(AlertComponent, '') + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(AlertComponent); + comp = fixture.componentInstance; + mockAlertService = TestBed.inject(AlertService); + }); + + it('Should call alertService.get on init', () => { + // WHEN + comp.ngOnInit(); + + // THEN + expect(mockAlertService.get).toHaveBeenCalled(); + }); + + it('Should call alertService.clear on destroy', () => { + // WHEN + comp.ngOnDestroy(); + + // THEN + expect(mockAlertService.clear).toHaveBeenCalled(); + }); +}); diff --git a/src/main/webapp/app/shared/alert/alert.component.ts b/src/main/webapp/app/shared/alert/alert.component.ts new file mode 100644 index 00000000..2f8461c3 --- /dev/null +++ b/src/main/webapp/app/shared/alert/alert.component.ts @@ -0,0 +1,33 @@ +import { Component, OnDestroy, OnInit } from '@angular/core'; + +import { AlertService, Alert } from 'app/core/util/alert.service'; + +@Component({ + selector: 'jhi-alert', + templateUrl: './alert.component.html', +}) +export class AlertComponent implements OnInit, OnDestroy { + alerts: Alert[] = []; + + constructor(private alertService: AlertService) {} + + ngOnInit(): void { + this.alerts = this.alertService.get(); + } + + setClasses(alert: Alert): { [key: string]: boolean } { + const classes = { 'jhi-toast': Boolean(alert.toast) }; + if (alert.position) { + return { ...classes, [alert.position]: true }; + } + return classes; + } + + ngOnDestroy(): void { + this.alertService.clear(); + } + + close(alert: Alert): void { + alert.close?.(this.alerts); + } +} diff --git a/src/main/webapp/app/shared/auth/has-any-authority.directive.spec.ts b/src/main/webapp/app/shared/auth/has-any-authority.directive.spec.ts new file mode 100644 index 00000000..0fad8baa --- /dev/null +++ b/src/main/webapp/app/shared/auth/has-any-authority.directive.spec.ts @@ -0,0 +1,130 @@ +jest.mock('app/core/auth/account.service'); + +import { Component, ElementRef, ViewChild } from '@angular/core'; +import { TestBed, waitForAsync } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; +import { Subject } from 'rxjs'; + +import { AccountService } from 'app/core/auth/account.service'; +import { Account } from 'app/core/auth/account.model'; + +import { HasAnyAuthorityDirective } from './has-any-authority.directive'; + +@Component({ + template: `
`, +}) +class TestHasAnyAuthorityDirectiveComponent { + @ViewChild('content', { static: false }) + content?: ElementRef; +} + +describe('HasAnyAuthorityDirective tests', () => { + let mockAccountService: AccountService; + const authenticationState = new Subject(); + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [HasAnyAuthorityDirective, TestHasAnyAuthorityDirectiveComponent], + providers: [AccountService], + }); + })); + + beforeEach(() => { + mockAccountService = TestBed.inject(AccountService); + mockAccountService.getAuthenticationState = jest.fn(() => authenticationState.asObservable()); + }); + + describe('set jhiHasAnyAuthority', () => { + it('should show restricted content to user if user has required role', () => { + // GIVEN + mockAccountService.hasAnyAuthority = jest.fn(() => true); + const fixture = TestBed.createComponent(TestHasAnyAuthorityDirectiveComponent); + const comp = fixture.componentInstance; + + // WHEN + fixture.detectChanges(); + + // THEN + expect(comp.content).toBeDefined(); + }); + + it('should not show restricted content to user if user has not required role', () => { + // GIVEN + mockAccountService.hasAnyAuthority = jest.fn(() => false); + const fixture = TestBed.createComponent(TestHasAnyAuthorityDirectiveComponent); + const comp = fixture.componentInstance; + + // WHEN + fixture.detectChanges(); + + // THEN + expect(comp.content).toBeUndefined(); + }); + }); + + describe('change authorities', () => { + it('should show or not show restricted content correctly if user authorities are changing', () => { + // GIVEN + mockAccountService.hasAnyAuthority = jest.fn(() => true); + const fixture = TestBed.createComponent(TestHasAnyAuthorityDirectiveComponent); + const comp = fixture.componentInstance; + + // WHEN + fixture.detectChanges(); + + // THEN + expect(comp.content).toBeDefined(); + + // GIVEN + mockAccountService.hasAnyAuthority = jest.fn(() => false); + + // WHEN + authenticationState.next(null); + fixture.detectChanges(); + + // THEN + expect(comp.content).toBeUndefined(); + + // GIVEN + mockAccountService.hasAnyAuthority = jest.fn(() => true); + + // WHEN + authenticationState.next(null); + fixture.detectChanges(); + + // THEN + expect(comp.content).toBeDefined(); + }); + }); + + describe('ngOnDestroy', () => { + it('should destroy authentication state subscription on component destroy', () => { + // GIVEN + mockAccountService.hasAnyAuthority = jest.fn(() => true); + const fixture = TestBed.createComponent(TestHasAnyAuthorityDirectiveComponent); + const div = fixture.debugElement.queryAllNodes(By.directive(HasAnyAuthorityDirective))[0]; + const hasAnyAuthorityDirective = div.injector.get(HasAnyAuthorityDirective); + + // WHEN + fixture.detectChanges(); + + // THEN + expect(mockAccountService.hasAnyAuthority).toHaveBeenCalled(); + + // WHEN + jest.clearAllMocks(); + authenticationState.next(null); + + // THEN + expect(mockAccountService.hasAnyAuthority).toHaveBeenCalled(); + + // WHEN + jest.clearAllMocks(); + hasAnyAuthorityDirective.ngOnDestroy(); + authenticationState.next(null); + + // THEN + expect(mockAccountService.hasAnyAuthority).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/src/main/webapp/app/shared/auth/has-any-authority.directive.ts b/src/main/webapp/app/shared/auth/has-any-authority.directive.ts new file mode 100644 index 00000000..1e47438b --- /dev/null +++ b/src/main/webapp/app/shared/auth/has-any-authority.directive.ts @@ -0,0 +1,53 @@ +import { Directive, Input, TemplateRef, ViewContainerRef, OnDestroy } from '@angular/core'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; + +import { AccountService } from 'app/core/auth/account.service'; + +/** + * @whatItDoes Conditionally includes an HTML element if current user has any + * of the authorities passed as the `expression`. + * + * @howToUse + * ``` + * ... + * + * ... + * ``` + */ +@Directive({ + selector: '[jhiHasAnyAuthority]', +}) +export class HasAnyAuthorityDirective implements OnDestroy { + private authorities!: string | string[]; + + private readonly destroy$ = new Subject(); + + constructor(private accountService: AccountService, private templateRef: TemplateRef, private viewContainerRef: ViewContainerRef) {} + + @Input() + set jhiHasAnyAuthority(value: string | string[]) { + this.authorities = value; + this.updateView(); + // Get notified each time authentication state changes. + this.accountService + .getAuthenticationState() + .pipe(takeUntil(this.destroy$)) + .subscribe(() => { + this.updateView(); + }); + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } + + private updateView(): void { + const hasAnyAuthority = this.accountService.hasAnyAuthority(this.authorities); + this.viewContainerRef.clear(); + if (hasAnyAuthority) { + this.viewContainerRef.createEmbeddedView(this.templateRef); + } + } +} diff --git a/src/main/webapp/app/shared/date/duration.pipe.ts b/src/main/webapp/app/shared/date/duration.pipe.ts new file mode 100644 index 00000000..a1a583b7 --- /dev/null +++ b/src/main/webapp/app/shared/date/duration.pipe.ts @@ -0,0 +1,15 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +import dayjs from 'dayjs/esm'; + +@Pipe({ + name: 'duration', +}) +export class DurationPipe implements PipeTransform { + transform(value: any): string { + if (value) { + return dayjs.duration(value).humanize(); + } + return ''; + } +} diff --git a/src/main/webapp/app/shared/date/format-medium-date.pipe.spec.ts b/src/main/webapp/app/shared/date/format-medium-date.pipe.spec.ts new file mode 100644 index 00000000..2d7b22e0 --- /dev/null +++ b/src/main/webapp/app/shared/date/format-medium-date.pipe.spec.ts @@ -0,0 +1,19 @@ +import dayjs from 'dayjs/esm'; + +import { FormatMediumDatePipe } from './format-medium-date.pipe'; + +describe('FormatMediumDatePipe', () => { + const formatMediumDatePipe = new FormatMediumDatePipe(); + + it('should return an empty string when receive undefined', () => { + expect(formatMediumDatePipe.transform(undefined)).toBe(''); + }); + + it('should return an empty string when receive null', () => { + expect(formatMediumDatePipe.transform(null)).toBe(''); + }); + + it('should format date like this D MMM YYYY', () => { + expect(formatMediumDatePipe.transform(dayjs('2020-11-16').locale('fr'))).toBe('16 Nov 2020'); + }); +}); diff --git a/src/main/webapp/app/shared/date/format-medium-date.pipe.ts b/src/main/webapp/app/shared/date/format-medium-date.pipe.ts new file mode 100644 index 00000000..fe59b88a --- /dev/null +++ b/src/main/webapp/app/shared/date/format-medium-date.pipe.ts @@ -0,0 +1,12 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +import dayjs from 'dayjs/esm'; + +@Pipe({ + name: 'formatMediumDate', +}) +export class FormatMediumDatePipe implements PipeTransform { + transform(day: dayjs.Dayjs | null | undefined): string { + return day ? day.format('D MMM YYYY') : ''; + } +} diff --git a/src/main/webapp/app/shared/date/format-medium-datetime.pipe.spec.ts b/src/main/webapp/app/shared/date/format-medium-datetime.pipe.spec.ts new file mode 100644 index 00000000..028cdc9b --- /dev/null +++ b/src/main/webapp/app/shared/date/format-medium-datetime.pipe.spec.ts @@ -0,0 +1,19 @@ +import dayjs from 'dayjs/esm'; + +import { FormatMediumDatetimePipe } from './format-medium-datetime.pipe'; + +describe('FormatMediumDatePipe', () => { + const formatMediumDatetimePipe = new FormatMediumDatetimePipe(); + + it('should return an empty string when receive undefined', () => { + expect(formatMediumDatetimePipe.transform(undefined)).toBe(''); + }); + + it('should return an empty string when receive null', () => { + expect(formatMediumDatetimePipe.transform(null)).toBe(''); + }); + + it('should format date like this D MMM YYYY', () => { + expect(formatMediumDatetimePipe.transform(dayjs('2020-11-16').locale('fr'))).toBe('16 Nov 2020 00:00:00'); + }); +}); diff --git a/src/main/webapp/app/shared/date/format-medium-datetime.pipe.ts b/src/main/webapp/app/shared/date/format-medium-datetime.pipe.ts new file mode 100644 index 00000000..919be022 --- /dev/null +++ b/src/main/webapp/app/shared/date/format-medium-datetime.pipe.ts @@ -0,0 +1,12 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +import dayjs from 'dayjs/esm'; + +@Pipe({ + name: 'formatMediumDatetime', +}) +export class FormatMediumDatetimePipe implements PipeTransform { + transform(day: dayjs.Dayjs | null | undefined): string { + return day ? day.format('D MMM YYYY HH:mm:ss') : ''; + } +} diff --git a/src/main/webapp/app/shared/filter/filter.component.html b/src/main/webapp/app/shared/filter/filter.component.html new file mode 100644 index 00000000..092a9319 --- /dev/null +++ b/src/main/webapp/app/shared/filter/filter.component.html @@ -0,0 +1,12 @@ +
+ Following filters are set + +
    + +
  • + {{ filterOption.name }}: {{ value }} + +
  • +
    +
+
diff --git a/src/main/webapp/app/shared/filter/filter.component.ts b/src/main/webapp/app/shared/filter/filter.component.ts new file mode 100644 index 00000000..97ca5f7c --- /dev/null +++ b/src/main/webapp/app/shared/filter/filter.component.ts @@ -0,0 +1,18 @@ +import { Component, Input } from '@angular/core'; +import { IFilterOptions } from './filter.model'; + +@Component({ + selector: 'jhi-filter', + templateUrl: './filter.component.html', +}) +export class FilterComponent { + @Input() filters!: IFilterOptions; + + clearAllFilters(): void { + this.filters.clear(); + } + + clearFilter(filterName: string, value: string): void { + this.filters.removeFilter(filterName, value); + } +} diff --git a/src/main/webapp/app/shared/filter/filter.model.spec.ts b/src/main/webapp/app/shared/filter/filter.model.spec.ts new file mode 100644 index 00000000..6c42c911 --- /dev/null +++ b/src/main/webapp/app/shared/filter/filter.model.spec.ts @@ -0,0 +1,242 @@ +import { convertToParamMap, ParamMap, Params } from '@angular/router'; +import { FilterOptions, FilterOption } from './filter.model'; + +describe('FilterModel Tests', () => { + describe('FilterOption', () => { + let filterOption: FilterOption; + + beforeEach(() => { + filterOption = new FilterOption('foo', ['bar', 'bar2']); + }); + + it('nameAsQueryParam returns query key', () => { + expect(filterOption.nameAsQueryParam()).toEqual('filter[foo]'); + }); + + describe('addValue', () => { + it('adds multiples unique values and returns true', () => { + const ret = filterOption.addValue('bar2', 'bar3', 'bar4'); + expect(filterOption.values).toMatchObject(['bar', 'bar2', 'bar3', 'bar4']); + expect(ret).toBe(true); + }); + it("doesn't adds duplicated values and return false", () => { + const ret = filterOption.addValue('bar', 'bar2'); + expect(filterOption.values).toMatchObject(['bar', 'bar2']); + expect(ret).toBe(false); + }); + }); + + describe('removeValue', () => { + it('removes the exiting value and return true', () => { + const ret = filterOption.removeValue('bar'); + expect(filterOption.values).toMatchObject(['bar2']); + expect(ret).toBe(true); + }); + it("doesn't removes the value and return false", () => { + const ret = filterOption.removeValue('foo'); + expect(filterOption.values).toMatchObject(['bar', 'bar2']); + expect(ret).toBe(false); + }); + }); + + describe('equals', () => { + it('returns true to matching options', () => { + const otherFilterOption = new FilterOption(filterOption.name, filterOption.values.concat()); + expect(filterOption.equals(otherFilterOption)).toBe(true); + expect(otherFilterOption.equals(filterOption)).toBe(true); + }); + it('returns false to different name', () => { + const otherFilterOption = new FilterOption('bar', filterOption.values.concat()); + expect(filterOption.equals(otherFilterOption)).toBe(false); + expect(otherFilterOption.equals(filterOption)).toBe(false); + }); + it('returns false to different values', () => { + const otherFilterOption = new FilterOption('bar', []); + expect(filterOption.equals(otherFilterOption)).toBe(false); + expect(otherFilterOption.equals(filterOption)).toBe(false); + }); + }); + }); + + describe('FilterOptions', () => { + describe('hasAnyFilterSet', () => { + it('with empty options returns false', () => { + const filters = new FilterOptions(); + expect(filters.hasAnyFilterSet()).toBe(false); + }); + it('with options and empty values returns false', () => { + const filters = new FilterOptions([new FilterOption('foo'), new FilterOption('bar')]); + expect(filters.hasAnyFilterSet()).toBe(false); + }); + it('with option and value returns true', () => { + const filters = new FilterOptions([new FilterOption('foo', ['bar'])]); + expect(filters.hasAnyFilterSet()).toBe(true); + }); + }); + + describe('clear', () => { + it("removes empty filters and dosn't emit next element", () => { + const filters = new FilterOptions([new FilterOption('foo'), new FilterOption('bar')]); + jest.spyOn(filters.filterChanges, 'next'); + + filters.clear(); + + expect(filters.filterChanges.next).not.toBeCalled(); + expect(filters.filterOptions).toMatchObject([]); + }); + it('removes empty filters and emits next element', () => { + const filters = new FilterOptions([new FilterOption('foo', ['existingFoo1']), new FilterOption('bar')]); + jest.spyOn(filters.filterChanges, 'next'); + + filters.clear(); + + expect(filters.filterChanges.next).toHaveBeenCalledTimes(1); + expect(filters.filterOptions).toMatchObject([]); + }); + }); + + describe('addFilter', () => { + it('adds a non existing FilterOption, returns true and emit next element', () => { + const filters = new FilterOptions([new FilterOption('foo', ['existingFoo1', 'existingFoo2']), new FilterOption('bar')]); + jest.spyOn(filters.filterChanges, 'next'); + + const result = filters.addFilter('addedFilter', 'addedValue'); + + expect(result).toBe(true); + expect(filters.filterChanges.next).toHaveBeenCalledTimes(1); + expect(filters.filterOptions).toMatchObject([ + { name: 'foo', values: ['existingFoo1', 'existingFoo2'] }, + { name: 'addedFilter', values: ['addedValue'] }, + ]); + }); + it('adds a non existing value to FilterOption, returns true and emit next element', () => { + const filters = new FilterOptions([new FilterOption('foo', ['existingFoo1', 'existingFoo2']), new FilterOption('bar')]); + jest.spyOn(filters.filterChanges, 'next'); + + const result = filters.addFilter('foo', 'addedValue1', 'addedValue2'); + + expect(result).toBe(true); + expect(filters.filterChanges.next).toHaveBeenCalledTimes(1); + expect(filters.filterOptions).toMatchObject([ + { name: 'foo', values: ['existingFoo1', 'existingFoo2', 'addedValue1', 'addedValue2'] }, + ]); + }); + it("doesn't add FilterOption values already added, returns false and doesn't emit next element", () => { + const filters = new FilterOptions([new FilterOption('foo', ['existingFoo1', 'existingFoo2']), new FilterOption('bar')]); + jest.spyOn(filters.filterChanges, 'next'); + + const result = filters.addFilter('foo', 'existingFoo1', 'existingFoo2'); + + expect(result).toBe(false); + expect(filters.filterChanges.next).not.toBeCalled(); + expect(filters.filterOptions).toMatchObject([{ name: 'foo', values: ['existingFoo1', 'existingFoo2'] }]); + }); + }); + + describe('removeFilter', () => { + it('removes an existing FilterOptions and returns true', () => { + const filters = new FilterOptions([new FilterOption('foo', ['existingFoo1', 'existingFoo2']), new FilterOption('bar')]); + jest.spyOn(filters.filterChanges, 'next'); + + const result = filters.removeFilter('foo', 'existingFoo1'); + + expect(result).toBe(true); + expect(filters.filterChanges.next).toHaveBeenCalledTimes(1); + expect(filters.filterOptions).toMatchObject([{ name: 'foo', values: ['existingFoo2'] }]); + }); + it("doesn't remove a non existing FilterOptions values returns false", () => { + const filters = new FilterOptions([new FilterOption('foo', ['existingFoo1', 'existingFoo2']), new FilterOption('bar')]); + jest.spyOn(filters.filterChanges, 'next'); + + const result = filters.removeFilter('foo', 'nonExisting1'); + + expect(result).toBe(false); + expect(filters.filterChanges.next).not.toBeCalled(); + expect(filters.filterOptions).toMatchObject([{ name: 'foo', values: ['existingFoo1', 'existingFoo2'] }]); + }); + it("doesn't remove a non existing FilterOptions returns false", () => { + const filters = new FilterOptions([new FilterOption('foo', ['existingFoo1', 'existingFoo2']), new FilterOption('bar')]); + jest.spyOn(filters.filterChanges, 'next'); + + const result = filters.removeFilter('nonExisting', 'nonExisting1'); + + expect(result).toBe(false); + expect(filters.filterChanges.next).not.toBeCalled(); + expect(filters.filterOptions).toMatchObject([{ name: 'foo', values: ['existingFoo1', 'existingFoo2'] }]); + }); + }); + + describe('initializeFromParams', () => { + const oneValidParam: Params = { + test: 'blub', + 'filter[hello.in]': 'world', + 'filter[invalid': 'invalid', + filter_invalid2: 'invalid', + }; + + const noValidParam: Params = { + test: 'blub', + 'filter[invalid': 'invalid', + filter_invalid2: 'invalid', + }; + + const paramWithTwoValues: Params = { + 'filter[hello.in]': ['world', 'world2'], + }; + + const paramWithTwoKeys: Params = { + 'filter[hello.in]': ['world', 'world2'], + 'filter[hello.notIn]': ['world3', 'world4'], + }; + + it('should parse from Params if there are any and not emit next element', () => { + const filters: FilterOptions = new FilterOptions([new FilterOption('foo', ['bar'])]); + jest.spyOn(filters.filterChanges, 'next'); + const paramMap: ParamMap = convertToParamMap(oneValidParam); + + filters.initializeFromParams(paramMap); + + expect(filters.filterChanges.next).not.toHaveBeenCalled(); + expect(filters.filterOptions).toMatchObject([{ name: 'hello.in', values: ['world'] }]); + }); + + it('should parse from Params and have none if there are none', () => { + const filters: FilterOptions = new FilterOptions(); + const paramMap: ParamMap = convertToParamMap(noValidParam); + jest.spyOn(filters.filterChanges, 'next'); + + filters.initializeFromParams(paramMap); + + expect(filters.filterChanges.next).not.toHaveBeenCalled(); + expect(filters.filterOptions).toMatchObject([]); + }); + + it('should parse from Params and have a parameter with 2 values and one aditional value', () => { + const filters: FilterOptions = new FilterOptions([new FilterOption('hello.in', ['world'])]); + jest.spyOn(filters.filterChanges, 'next'); + + const paramMap: ParamMap = convertToParamMap(paramWithTwoValues); + + filters.initializeFromParams(paramMap); + + expect(filters.filterChanges.next).not.toHaveBeenCalled(); + expect(filters.filterOptions).toMatchObject([{ name: 'hello.in', values: ['world', 'world2'] }]); + }); + + it('should parse from Params and have a parameter with 2 keys', () => { + const filters: FilterOptions = new FilterOptions(); + jest.spyOn(filters.filterChanges, 'next'); + + const paramMap: ParamMap = convertToParamMap(paramWithTwoKeys); + + filters.initializeFromParams(paramMap); + + expect(filters.filterChanges.next).not.toHaveBeenCalled(); + expect(filters.filterOptions).toMatchObject([ + { name: 'hello.in', values: ['world', 'world2'] }, + { name: 'hello.notIn', values: ['world3', 'world4'] }, + ]); + }); + }); + }); +}); diff --git a/src/main/webapp/app/shared/filter/filter.model.ts b/src/main/webapp/app/shared/filter/filter.model.ts new file mode 100644 index 00000000..a01ddaee --- /dev/null +++ b/src/main/webapp/app/shared/filter/filter.model.ts @@ -0,0 +1,156 @@ +import { ParamMap } from '@angular/router'; +import { Subject } from 'rxjs'; + +export interface IFilterOptions { + readonly filterChanges: Subject; + get filterOptions(): IFilterOption[]; + hasAnyFilterSet(): boolean; + clear(): boolean; + initializeFromParams(params: ParamMap): boolean; + addFilter(name: string, ...values: string[]): boolean; + removeFilter(name: string, value: string): boolean; +} + +export interface IFilterOption { + name: string; + values: string[]; + nameAsQueryParam(): string; +} + +export class FilterOption implements IFilterOption { + constructor(public name: string, public values: string[] = []) { + this.values = [...new Set(values)]; + } + + nameAsQueryParam(): string { + return 'filter[' + this.name + ']'; + } + + isSet(): boolean { + return this.values.length > 0; + } + + addValue(...values: string[]): boolean { + const missingValues = values.filter(value => value && !this.values.includes(value)); + if (missingValues.length > 0) { + this.values.push(...missingValues); + return true; + } + return false; + } + + removeValue(value: string): boolean { + const indexOf = this.values.indexOf(value); + if (indexOf === -1) { + return false; + } + + this.values.splice(indexOf, 1); + return true; + } + + clone(): FilterOption { + return new FilterOption(this.name, this.values.concat()); + } + + equals(other: IFilterOption): boolean { + return ( + this.name === other.name && + this.values.length === other.values.length && + this.values.every(thisValue => other.values.includes(thisValue)) && + other.values.every(otherValue => this.values.includes(otherValue)) + ); + } +} + +export class FilterOptions implements IFilterOptions { + readonly filterChanges: Subject = new Subject(); + private _filterOptions: FilterOption[]; + + constructor(filterOptions: FilterOption[] = []) { + this._filterOptions = filterOptions; + } + + get filterOptions(): FilterOption[] { + return this._filterOptions.filter(option => option.isSet()); + } + + hasAnyFilterSet(): boolean { + return this._filterOptions.some(e => e.isSet()); + } + + clear(): boolean { + const hasFields = this.hasAnyFilterSet(); + this._filterOptions = []; + if (hasFields) { + this.changed(); + } + return hasFields; + } + + initializeFromParams(params: ParamMap): boolean { + const oldFilters: FilterOptions = this.clone(); + + this._filterOptions = []; + + const filterRegex = /filter\[(.+)\]/; + params.keys + .filter(paramKey => filterRegex.test(paramKey)) + .forEach(matchingParam => { + const matches = matchingParam.match(filterRegex); + if (matches && matches.length > 1) { + this.getFilterOptionByName(matches[1], true).addValue(...params.getAll(matchingParam)); + } + }); + + if (oldFilters.equals(this)) { + return false; + } + return true; + } + + addFilter(name: string, ...values: string[]): boolean { + if (this.getFilterOptionByName(name, true).addValue(...values)) { + this.changed(); + return true; + } + return false; + } + + removeFilter(name: string, value: string): boolean { + if (this.getFilterOptionByName(name)?.removeValue(value)) { + this.changed(); + return true; + } + return false; + } + + protected changed(): void { + this.filterChanges.next(this.filterOptions.map(option => option.clone())); + } + + protected equals(other: FilterOptions): boolean { + const thisFilters = this.filterOptions; + const otherFilters = other.filterOptions; + if (thisFilters.length !== otherFilters.length) { + return false; + } + return thisFilters.every(option => other.getFilterOptionByName(option.name)?.equals(option)); + } + + protected clone(): FilterOptions { + return new FilterOptions(this.filterOptions.map(option => new FilterOption(option.name, option.values.concat()))); + } + + protected getFilterOptionByName(name: string, add: true): FilterOption; + protected getFilterOptionByName(name: string, add: false): FilterOption | null; + protected getFilterOptionByName(name: string): FilterOption | null; + protected getFilterOptionByName(name: string, add = false): FilterOption | null { + const addOption = (option: FilterOption): FilterOption => { + this._filterOptions.push(option); + return option; + }; + + return this._filterOptions.find(thisOption => thisOption.name === name) ?? (add ? addOption(new FilterOption(name)) : null); + } +} diff --git a/src/main/webapp/app/shared/language/find-language-from-key.pipe.ts b/src/main/webapp/app/shared/language/find-language-from-key.pipe.ts new file mode 100644 index 00000000..736809cf --- /dev/null +++ b/src/main/webapp/app/shared/language/find-language-from-key.pipe.ts @@ -0,0 +1,14 @@ +import { Pipe, PipeTransform } from '@angular/core'; + +@Pipe({ name: 'findLanguageFromKey' }) +export class FindLanguageFromKeyPipe implements PipeTransform { + private languages: { [key: string]: { name: string; rtl?: boolean } } = { + en: { name: 'English' }, + fr: { name: 'Français' }, + // jhipster-needle-i18n-language-key-pipe - JHipster will add/remove languages in this object + }; + + transform(lang: string): string { + return this.languages[lang].name; + } +} diff --git a/src/main/webapp/app/shared/language/translate.directive.spec.ts b/src/main/webapp/app/shared/language/translate.directive.spec.ts new file mode 100644 index 00000000..341f6891 --- /dev/null +++ b/src/main/webapp/app/shared/language/translate.directive.spec.ts @@ -0,0 +1,35 @@ +import { Component } from '@angular/core'; +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { TranslateModule, TranslateService } from '@ngx-translate/core'; + +import { TranslateDirective } from './translate.directive'; + +@Component({ + template: `
`, +}) +class TestTranslateDirectiveComponent {} + +describe('TranslateDirective Tests', () => { + let fixture: ComponentFixture; + let translateService: TranslateService; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot()], + declarations: [TranslateDirective, TestTranslateDirectiveComponent], + }); + })); + + beforeEach(() => { + translateService = TestBed.inject(TranslateService); + fixture = TestBed.createComponent(TestTranslateDirectiveComponent); + }); + + it('should change HTML', () => { + const spy = jest.spyOn(translateService, 'get'); + + fixture.detectChanges(); + + expect(spy).toHaveBeenCalled(); + }); +}); diff --git a/src/main/webapp/app/shared/language/translate.directive.ts b/src/main/webapp/app/shared/language/translate.directive.ts new file mode 100644 index 00000000..1c3a4098 --- /dev/null +++ b/src/main/webapp/app/shared/language/translate.directive.ts @@ -0,0 +1,51 @@ +import { Input, Directive, ElementRef, OnChanges, OnInit, OnDestroy } from '@angular/core'; +import { TranslateService } from '@ngx-translate/core'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; + +import { translationNotFoundMessage } from 'app/config/translation.config'; + +/** + * A wrapper directive on top of the translate pipe as the inbuilt translate directive from ngx-translate is too verbose and buggy + */ +@Directive({ + selector: '[jhiTranslate]', +}) +export class TranslateDirective implements OnChanges, OnInit, OnDestroy { + @Input() jhiTranslate!: string; + @Input() translateValues?: { [key: string]: unknown }; + + private readonly directiveDestroyed = new Subject(); + + constructor(private el: ElementRef, private translateService: TranslateService) {} + + ngOnInit(): void { + this.translateService.onLangChange.pipe(takeUntil(this.directiveDestroyed)).subscribe(() => { + this.getTranslation(); + }); + this.translateService.onTranslationChange.pipe(takeUntil(this.directiveDestroyed)).subscribe(() => { + this.getTranslation(); + }); + } + + ngOnChanges(): void { + this.getTranslation(); + } + + ngOnDestroy(): void { + this.directiveDestroyed.next(null); + this.directiveDestroyed.complete(); + } + + private getTranslation(): void { + this.translateService + .get(this.jhiTranslate, this.translateValues) + .pipe(takeUntil(this.directiveDestroyed)) + .subscribe({ + next: value => { + this.el.nativeElement.innerHTML = value; + }, + error: () => `${translationNotFoundMessage}[${this.jhiTranslate}]`, + }); + } +} diff --git a/src/main/webapp/app/shared/language/translation.module.ts b/src/main/webapp/app/shared/language/translation.module.ts new file mode 100644 index 00000000..c6fad34d --- /dev/null +++ b/src/main/webapp/app/shared/language/translation.module.ts @@ -0,0 +1,29 @@ +import { NgModule } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { TranslateModule, TranslateService, TranslateLoader, MissingTranslationHandler } from '@ngx-translate/core'; +import { translatePartialLoader, missingTranslationHandler } from 'app/config/translation.config'; +import { SessionStorageService } from 'ngx-webstorage'; + +@NgModule({ + imports: [ + TranslateModule.forRoot({ + loader: { + provide: TranslateLoader, + useFactory: translatePartialLoader, + deps: [HttpClient], + }, + missingTranslationHandler: { + provide: MissingTranslationHandler, + useFactory: missingTranslationHandler, + }, + }), + ], +}) +export class TranslationModule { + constructor(private translateService: TranslateService, sessionStorageService: SessionStorageService) { + translateService.setDefaultLang('en'); + // if user have changed language and navigates away from the application and back to the application then use previously choosed language + const langKey = sessionStorageService.retrieve('locale') ?? 'en'; + translateService.use(langKey); + } +} diff --git a/src/main/webapp/app/shared/pagination/item-count.component.spec.ts b/src/main/webapp/app/shared/pagination/item-count.component.spec.ts new file mode 100644 index 00000000..91589bee --- /dev/null +++ b/src/main/webapp/app/shared/pagination/item-count.component.spec.ts @@ -0,0 +1,68 @@ +import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing'; +import { TranslateModule } from '@ngx-translate/core'; + +import { TranslateDirective } from 'app/shared/language/translate.directive'; + +import { ItemCountComponent } from './item-count.component'; + +describe('ItemCountComponent test', () => { + let comp: ItemCountComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + imports: [TranslateModule.forRoot()], + declarations: [ItemCountComponent, TranslateDirective], + }).compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(ItemCountComponent); + comp = fixture.componentInstance; + }); + + describe('UI logic tests', () => { + it('should initialize with undefined', () => { + expect(comp.first).toBeUndefined(); + expect(comp.second).toBeUndefined(); + expect(comp.total).toBeUndefined(); + }); + + it('should set calculated numbers to undefined if the page value is not yet defined', () => { + // GIVEN + comp.params = { page: undefined, totalItems: 0, itemsPerPage: 10 }; + + // THEN + expect(comp.first).toBeUndefined(); + expect(comp.second).toBeUndefined(); + }); + + it('should change the content on page change', () => { + // GIVEN + comp.params = { page: 1, totalItems: 100, itemsPerPage: 10 }; + + // THEN + expect(comp.first).toBe(1); + expect(comp.second).toBe(10); + expect(comp.total).toBe(100); + + // GIVEN + comp.params = { page: 2, totalItems: 100, itemsPerPage: 10 }; + + // THEN + expect(comp.first).toBe(11); + expect(comp.second).toBe(20); + expect(comp.total).toBe(100); + }); + + it('should set the second number to totalItems if this is the last page which contains less than itemsPerPage items', () => { + // GIVEN + comp.params = { page: 2, totalItems: 16, itemsPerPage: 10 }; + + // THEN + expect(comp.first).toBe(11); + expect(comp.second).toBe(16); + expect(comp.total).toBe(16); + }); + }); +}); diff --git a/src/main/webapp/app/shared/pagination/item-count.component.ts b/src/main/webapp/app/shared/pagination/item-count.component.ts new file mode 100644 index 00000000..ca05ebb5 --- /dev/null +++ b/src/main/webapp/app/shared/pagination/item-count.component.ts @@ -0,0 +1,31 @@ +import { Component, Input } from '@angular/core'; + +/** + * A component that will take care of item count statistics of a pagination. + */ +@Component({ + selector: 'jhi-item-count', + template: `
`, +}) +export class ItemCountComponent { + /** + * @param params Contains parameters for component: + * page Current page number + * totalItems Total number of items + * itemsPerPage Number of items per page + */ + @Input() set params(params: { page?: number; totalItems?: number; itemsPerPage?: number }) { + if (params.page && params.totalItems !== undefined && params.itemsPerPage) { + this.first = (params.page - 1) * params.itemsPerPage + 1; + this.second = params.page * params.itemsPerPage < params.totalItems ? params.page * params.itemsPerPage : params.totalItems; + } else { + this.first = undefined; + this.second = undefined; + } + this.total = params.totalItems; + } + + first?: number; + second?: number; + total?: number; +} diff --git a/src/main/webapp/app/shared/shared-libs.module.ts b/src/main/webapp/app/shared/shared-libs.module.ts new file mode 100644 index 00000000..368647c3 --- /dev/null +++ b/src/main/webapp/app/shared/shared-libs.module.ts @@ -0,0 +1,12 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { NgbModule } from '@ng-bootstrap/ng-bootstrap'; +import { InfiniteScrollModule } from 'ngx-infinite-scroll'; +import { FontAwesomeModule } from '@fortawesome/angular-fontawesome'; +import { TranslateModule } from '@ngx-translate/core'; + +@NgModule({ + exports: [FormsModule, CommonModule, NgbModule, InfiniteScrollModule, FontAwesomeModule, ReactiveFormsModule, TranslateModule], +}) +export class SharedLibsModule {} diff --git a/src/main/webapp/app/shared/shared.module.ts b/src/main/webapp/app/shared/shared.module.ts new file mode 100644 index 00000000..49fdbcf3 --- /dev/null +++ b/src/main/webapp/app/shared/shared.module.ts @@ -0,0 +1,49 @@ +import { NgModule } from '@angular/core'; + +import { SharedLibsModule } from './shared-libs.module'; +import { FindLanguageFromKeyPipe } from './language/find-language-from-key.pipe'; +import { TranslateDirective } from './language/translate.directive'; +import { AlertComponent } from './alert/alert.component'; +import { AlertErrorComponent } from './alert/alert-error.component'; +import { HasAnyAuthorityDirective } from './auth/has-any-authority.directive'; +import { DurationPipe } from './date/duration.pipe'; +import { FormatMediumDatetimePipe } from './date/format-medium-datetime.pipe'; +import { FormatMediumDatePipe } from './date/format-medium-date.pipe'; +import { SortByDirective } from './sort/sort-by.directive'; +import { SortDirective } from './sort/sort.directive'; +import { ItemCountComponent } from './pagination/item-count.component'; +import { FilterComponent } from './filter/filter.component'; + +@NgModule({ + imports: [SharedLibsModule], + declarations: [ + FindLanguageFromKeyPipe, + TranslateDirective, + AlertComponent, + AlertErrorComponent, + HasAnyAuthorityDirective, + DurationPipe, + FormatMediumDatetimePipe, + FormatMediumDatePipe, + SortByDirective, + SortDirective, + ItemCountComponent, + FilterComponent, + ], + exports: [ + SharedLibsModule, + FindLanguageFromKeyPipe, + TranslateDirective, + AlertComponent, + AlertErrorComponent, + HasAnyAuthorityDirective, + DurationPipe, + FormatMediumDatetimePipe, + FormatMediumDatePipe, + SortByDirective, + SortDirective, + ItemCountComponent, + FilterComponent, + ], +}) +export class SharedModule {} diff --git a/src/main/webapp/app/shared/sort/sort-by.directive.spec.ts b/src/main/webapp/app/shared/sort/sort-by.directive.spec.ts new file mode 100644 index 00000000..c9bc01c8 --- /dev/null +++ b/src/main/webapp/app/shared/sort/sort-by.directive.spec.ts @@ -0,0 +1,139 @@ +import { Component, DebugElement } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; +import { FaIconComponent, FaIconLibrary } from '@fortawesome/angular-fontawesome'; +import { fas, faSort, faSortDown, faSortUp } from '@fortawesome/free-solid-svg-icons'; + +import { SortByDirective } from './sort-by.directive'; +import { SortDirective } from './sort.directive'; + +@Component({ + template: ` + + + + + + +
ID
+ `, +}) +class TestSortByDirectiveComponent { + predicate?: string; + ascending?: boolean; + sortAllowed = true; + transition = jest.fn(); + + constructor(library: FaIconLibrary) { + library.addIconPacks(fas); + library.addIcons(faSort, faSortDown, faSortUp); + } +} + +describe('Directive: SortByDirective', () => { + let component: TestSortByDirectiveComponent; + let fixture: ComponentFixture; + let tableHead: DebugElement; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [TestSortByDirectiveComponent, SortDirective, SortByDirective, FaIconComponent], + }); + fixture = TestBed.createComponent(TestSortByDirectiveComponent); + component = fixture.componentInstance; + tableHead = fixture.debugElement.query(By.directive(SortByDirective)); + }); + + it('should initialize predicate, order, icon when initial component predicate differs from column predicate', () => { + // GIVEN + component.predicate = 'id'; + const sortByDirective = tableHead.injector.get(SortByDirective); + + // WHEN + fixture.detectChanges(); + + // THEN + expect(sortByDirective.jhiSortBy).toEqual('name'); + expect(component.predicate).toEqual('id'); + expect(sortByDirective.iconComponent?.icon).toEqual('sort'); + expect(component.transition).toHaveBeenCalledTimes(0); + }); + + it('should initialize predicate, order, icon when initial component predicate is same as column predicate', () => { + // GIVEN + component.predicate = 'name'; + component.ascending = true; + const sortByDirective = tableHead.injector.get(SortByDirective); + + // WHEN + fixture.detectChanges(); + + // THEN + expect(sortByDirective.jhiSortBy).toEqual('name'); + expect(component.predicate).toEqual('name'); + expect(component.ascending).toEqual(true); + expect(sortByDirective.iconComponent?.icon).toEqual(faSortUp.iconName); + expect(component.transition).toHaveBeenCalledTimes(0); + }); + + it('should update component predicate, order, icon when user clicks on column header', () => { + // GIVEN + component.predicate = 'name'; + component.ascending = true; + const sortByDirective = tableHead.injector.get(SortByDirective); + + // WHEN + fixture.detectChanges(); + tableHead.triggerEventHandler('click', null); + fixture.detectChanges(); + + // THEN + expect(component.predicate).toEqual('name'); + expect(component.ascending).toEqual(false); + expect(sortByDirective.iconComponent?.icon).toEqual(faSortDown.iconName); + expect(component.transition).toHaveBeenCalledTimes(1); + expect(component.transition).toHaveBeenCalledWith({ predicate: 'name', ascending: false }); + }); + + it('should update component predicate, order, icon when user double clicks on column header', () => { + // GIVEN + component.predicate = 'name'; + component.ascending = true; + const sortByDirective = tableHead.injector.get(SortByDirective); + + // WHEN + fixture.detectChanges(); + + tableHead.triggerEventHandler('click', null); + fixture.detectChanges(); + + tableHead.triggerEventHandler('click', null); + fixture.detectChanges(); + + // THEN + expect(component.predicate).toEqual('name'); + expect(component.ascending).toEqual(true); + expect(sortByDirective.iconComponent?.icon).toEqual(faSortUp.iconName); + expect(component.transition).toHaveBeenCalledTimes(2); + expect(component.transition).toHaveBeenNthCalledWith(1, { predicate: 'name', ascending: false }); + expect(component.transition).toHaveBeenNthCalledWith(2, { predicate: 'name', ascending: true }); + }); + + it('should not run sorting on click if sorting icon is hidden', () => { + // GIVEN + component.predicate = 'id'; + component.ascending = false; + component.sortAllowed = false; + + // WHEN + fixture.detectChanges(); + + tableHead.triggerEventHandler('click', null); + fixture.detectChanges(); + + // THEN + expect(component.predicate).toEqual('id'); + expect(component.ascending).toEqual(false); + expect(component.transition).not.toHaveBeenCalled(); + }); +}); diff --git a/src/main/webapp/app/shared/sort/sort-by.directive.ts b/src/main/webapp/app/shared/sort/sort-by.directive.ts new file mode 100644 index 00000000..8a444f8f --- /dev/null +++ b/src/main/webapp/app/shared/sort/sort-by.directive.ts @@ -0,0 +1,55 @@ +import { AfterContentInit, ContentChild, Directive, Host, HostListener, Input, OnDestroy } from '@angular/core'; +import { Subject } from 'rxjs'; +import { takeUntil } from 'rxjs/operators'; +import { FaIconComponent } from '@fortawesome/angular-fontawesome'; +import { faSort, faSortDown, faSortUp, IconDefinition } from '@fortawesome/free-solid-svg-icons'; + +import { SortDirective } from './sort.directive'; + +@Directive({ + selector: '[jhiSortBy]', +}) +export class SortByDirective implements AfterContentInit, OnDestroy { + @Input() jhiSortBy!: T; + + @ContentChild(FaIconComponent, { static: false }) + iconComponent?: FaIconComponent; + + sortIcon = faSort; + sortAscIcon = faSortUp; + sortDescIcon = faSortDown; + + private readonly destroy$ = new Subject(); + + constructor(@Host() private sort: SortDirective) { + sort.predicateChange.pipe(takeUntil(this.destroy$)).subscribe(() => this.updateIconDefinition()); + sort.ascendingChange.pipe(takeUntil(this.destroy$)).subscribe(() => this.updateIconDefinition()); + } + + @HostListener('click') + onClick(): void { + if (this.iconComponent) { + this.sort.sort(this.jhiSortBy); + } + } + + ngAfterContentInit(): void { + this.updateIconDefinition(); + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } + + private updateIconDefinition(): void { + if (this.iconComponent) { + let icon: IconDefinition = this.sortIcon; + if (this.sort.predicate === this.jhiSortBy) { + icon = this.sort.ascending ? this.sortAscIcon : this.sortDescIcon; + } + this.iconComponent.icon = icon.iconName; + this.iconComponent.render(); + } + } +} diff --git a/src/main/webapp/app/shared/sort/sort.directive.spec.ts b/src/main/webapp/app/shared/sort/sort.directive.spec.ts new file mode 100644 index 00000000..43017b83 --- /dev/null +++ b/src/main/webapp/app/shared/sort/sort.directive.spec.ts @@ -0,0 +1,86 @@ +import { Component, DebugElement } from '@angular/core'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { By } from '@angular/platform-browser'; + +import { SortDirective } from './sort.directive'; + +@Component({ + template: ` + + + + +
+ `, +}) +class TestSortDirectiveComponent { + predicate?: string; + ascending?: boolean; + transition = jest.fn(); +} + +describe('Directive: SortDirective', () => { + let component: TestSortDirectiveComponent; + let fixture: ComponentFixture; + let tableRow: DebugElement; + + beforeEach(() => { + TestBed.configureTestingModule({ + declarations: [TestSortDirectiveComponent, SortDirective], + }); + fixture = TestBed.createComponent(TestSortDirectiveComponent); + component = fixture.componentInstance; + tableRow = fixture.debugElement.query(By.directive(SortDirective)); + }); + + it('should update predicate, order and invoke sortChange function', () => { + // GIVEN + const sortDirective = tableRow.injector.get(SortDirective); + + // WHEN + fixture.detectChanges(); + sortDirective.sort('ID'); + + // THEN + expect(component.predicate).toEqual('ID'); + expect(component.ascending).toEqual(true); + expect(component.transition).toHaveBeenCalledTimes(1); + expect(component.transition).toHaveBeenCalledWith({ predicate: 'ID', ascending: true }); + }); + + it('should change sort order to descending when same field is sorted again', () => { + // GIVEN + const sortDirective = tableRow.injector.get(SortDirective); + + // WHEN + fixture.detectChanges(); + sortDirective.sort('ID'); + // sort again + sortDirective.sort('ID'); + + // THEN + expect(component.predicate).toEqual('ID'); + expect(component.ascending).toEqual(false); + expect(component.transition).toHaveBeenCalledTimes(2); + expect(component.transition).toHaveBeenNthCalledWith(1, { predicate: 'ID', ascending: true }); + expect(component.transition).toHaveBeenNthCalledWith(2, { predicate: 'ID', ascending: false }); + }); + + it('should change sort order to ascending when different field is sorted', () => { + // GIVEN + const sortDirective = tableRow.injector.get(SortDirective); + + // WHEN + fixture.detectChanges(); + sortDirective.sort('ID'); + // sort again + sortDirective.sort('NAME'); + + // THEN + expect(component.predicate).toEqual('NAME'); + expect(component.ascending).toEqual(true); + expect(component.transition).toHaveBeenCalledTimes(2); + expect(component.transition).toHaveBeenNthCalledWith(1, { predicate: 'ID', ascending: true }); + expect(component.transition).toHaveBeenNthCalledWith(2, { predicate: 'NAME', ascending: true }); + }); +}); diff --git a/src/main/webapp/app/shared/sort/sort.directive.ts b/src/main/webapp/app/shared/sort/sort.directive.ts new file mode 100644 index 00000000..c2a069b0 --- /dev/null +++ b/src/main/webapp/app/shared/sort/sort.directive.ts @@ -0,0 +1,39 @@ +import { Directive, EventEmitter, Input, Output } from '@angular/core'; + +@Directive({ + selector: '[jhiSort]', +}) +export class SortDirective { + @Input() + get predicate(): T | undefined { + return this._predicate; + } + set predicate(predicate: T | undefined) { + this._predicate = predicate; + this.predicateChange.emit(predicate); + } + + @Input() + get ascending(): boolean | undefined { + return this._ascending; + } + set ascending(ascending: boolean | undefined) { + this._ascending = ascending; + this.ascendingChange.emit(ascending); + } + + @Output() predicateChange = new EventEmitter(); + @Output() ascendingChange = new EventEmitter(); + @Output() sortChange = new EventEmitter<{ predicate: T; ascending: boolean }>(); + + private _predicate?: T; + private _ascending?: boolean; + + sort(field: T): void { + this.ascending = field !== this.predicate ? true : !this.ascending; + this.predicate = field; + this.predicateChange.emit(field); + this.ascendingChange.emit(this.ascending); + this.sortChange.emit({ predicate: this.predicate, ascending: this.ascending }); + } +} diff --git a/src/main/webapp/app/shared/sort/sort.service.ts b/src/main/webapp/app/shared/sort/sort.service.ts new file mode 100644 index 00000000..d2760599 --- /dev/null +++ b/src/main/webapp/app/shared/sort/sort.service.ts @@ -0,0 +1,13 @@ +import { Injectable } from '@angular/core'; + +@Injectable({ providedIn: 'root' }) +export class SortService { + private collator = new Intl.Collator(undefined, { + numeric: true, + sensitivity: 'base', + }); + + public startSort(property: string, order: number): (a: any, b: any) => number { + return (a: any, b: any) => this.collator.compare(a[property], b[property]) * order; + } +} diff --git a/src/main/webapp/bootstrap.ts b/src/main/webapp/bootstrap.ts new file mode 100644 index 00000000..e5038d52 --- /dev/null +++ b/src/main/webapp/bootstrap.ts @@ -0,0 +1,16 @@ +import { enableProdMode } from '@angular/core'; +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; + +import { DEBUG_INFO_ENABLED } from './app/app.constants'; +import { AppModule } from './app/app.module'; + +// disable debug data on prod profile to improve performance +if (!DEBUG_INFO_ENABLED) { + enableProdMode(); +} + +platformBrowserDynamic() + .bootstrapModule(AppModule, { preserveWhitespaces: true }) + // eslint-disable-next-line no-console + .then(() => console.log('Application started')) + .catch(err => console.error(err)); diff --git a/src/main/webapp/content/css/loading.css b/src/main/webapp/content/css/loading.css new file mode 100644 index 00000000..b2c66268 --- /dev/null +++ b/src/main/webapp/content/css/loading.css @@ -0,0 +1,152 @@ +@keyframes lds-pacman-1 { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 50% { + -webkit-transform: rotate(-45deg); + transform: rotate(-45deg); + } + 100% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } +} +@-webkit-keyframes lds-pacman-1 { + 0% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } + 50% { + -webkit-transform: rotate(-45deg); + transform: rotate(-45deg); + } + 100% { + -webkit-transform: rotate(0deg); + transform: rotate(0deg); + } +} +@keyframes lds-pacman-2 { + 0% { + -webkit-transform: rotate(180deg); + transform: rotate(180deg); + } + 50% { + -webkit-transform: rotate(225deg); + transform: rotate(225deg); + } + 100% { + -webkit-transform: rotate(180deg); + transform: rotate(180deg); + } +} +@-webkit-keyframes lds-pacman-2 { + 0% { + -webkit-transform: rotate(180deg); + transform: rotate(180deg); + } + 50% { + -webkit-transform: rotate(225deg); + transform: rotate(225deg); + } + 100% { + -webkit-transform: rotate(180deg); + transform: rotate(180deg); + } +} +@keyframes lds-pacman-3 { + 0% { + -webkit-transform: translate(190px, 0); + transform: translate(190px, 0); + opacity: 0; + } + 20% { + opacity: 1; + } + 100% { + -webkit-transform: translate(70px, 0); + transform: translate(70px, 0); + opacity: 1; + } +} +@-webkit-keyframes lds-pacman-3 { + 0% { + -webkit-transform: translate(190px, 0); + transform: translate(190px, 0); + opacity: 0; + } + 20% { + opacity: 1; + } + 100% { + -webkit-transform: translate(70px, 0); + transform: translate(70px, 0); + opacity: 1; + } +} + +.app-loading { + font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; + position: relative; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + top: 10em; +} +.app-loading p { + display: block; + font-size: 1.17em; + margin-inline-start: 0px; + margin-inline-end: 0px; + font-weight: normal; +} + +.app-loading .lds-pacman { + position: relative; + margin: auto; + width: 200px !important; + height: 200px !important; + -webkit-transform: translate(-100px, -100px) scale(1) translate(100px, 100px); + transform: translate(-100px, -100px) scale(1) translate(100px, 100px); +} +.app-loading .lds-pacman > div:nth-child(2) div { + position: absolute; + top: 40px; + left: 40px; + width: 120px; + height: 60px; + border-radius: 120px 120px 0 0; + background: #bbcedd; + -webkit-animation: lds-pacman-1 1s linear infinite; + animation: lds-pacman-1 1s linear infinite; + -webkit-transform-origin: 60px 60px; + transform-origin: 60px 60px; +} +.app-loading .lds-pacman > div:nth-child(2) div:nth-child(2) { + -webkit-animation: lds-pacman-2 1s linear infinite; + animation: lds-pacman-2 1s linear infinite; +} +.app-loading .lds-pacman > div:nth-child(1) div { + position: absolute; + top: 97px; + left: -8px; + width: 24px; + height: 10px; + background-image: url('../images/logo-jhipster.png'); + background-size: contain; + -webkit-animation: lds-pacman-3 1s linear infinite; + animation: lds-pacman-3 1.5s linear infinite; +} +.app-loading .lds-pacman > div:nth-child(1) div:nth-child(1) { + -webkit-animation-delay: -0.67s; + animation-delay: -1s; +} +.app-loading .lds-pacman > div:nth-child(1) div:nth-child(2) { + -webkit-animation-delay: -0.33s; + animation-delay: -0.5s; +} +.app-loading .lds-pacman > div:nth-child(1) div:nth-child(3) { + -webkit-animation-delay: 0s; + animation-delay: 0s; +} diff --git a/src/main/webapp/content/images/jhipster_family_member_0.svg b/src/main/webapp/content/images/jhipster_family_member_0.svg new file mode 100644 index 00000000..d6df83ce --- /dev/null +++ b/src/main/webapp/content/images/jhipster_family_member_0.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/content/images/jhipster_family_member_0_head-192.png b/src/main/webapp/content/images/jhipster_family_member_0_head-192.png new file mode 100644 index 0000000000000000000000000000000000000000..6d90ab36d37914257d5f3d0356e7895ccc0596f2 GIT binary patch literal 13439 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 literal 0 HcmV?d00001 diff --git a/src/main/webapp/content/images/jhipster_family_member_0_head-256.png b/src/main/webapp/content/images/jhipster_family_member_0_head-256.png new file mode 100644 index 0000000000000000000000000000000000000000..8e99bfe66d5a0e4831fcdc4031c1a57ba9b9cb1a GIT binary patch literal 7037 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 literal 0 HcmV?d00001 diff --git a/src/main/webapp/content/images/jhipster_family_member_0_head-384.png b/src/main/webapp/content/images/jhipster_family_member_0_head-384.png new file mode 100644 index 0000000000000000000000000000000000000000..c7ca46022548477085d9c10966c94131e7290df6 GIT binary patch literal 10350 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 literal 0 HcmV?d00001 diff --git a/src/main/webapp/content/images/jhipster_family_member_1.svg b/src/main/webapp/content/images/jhipster_family_member_1.svg new file mode 100644 index 00000000..e3a0f3db --- /dev/null +++ b/src/main/webapp/content/images/jhipster_family_member_1.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/content/images/jhipster_family_member_1_head-192.png b/src/main/webapp/content/images/jhipster_family_member_1_head-192.png new file mode 100644 index 0000000000000000000000000000000000000000..ac5f2a00ac809809f7e48628d79e04849ebad98b GIT binary patch literal 7046 zcma)hbxa(2TuSR38PG)x6jcqC)C~R2wG~tiWEJ%! z<#bgw44=_bOWR#vGr&+yLrFW{RH?v9p~_aS(N4D6PNvmCPEf%(SF2C^gtC)Z5Ezu;BbBv%NgbLAmaudcEb_w$DZ#fi^u6 zj!hqw+uXIsOCg6_8~^@Z{{4NpF)>tY@@6DE`fy=#tG5M-L{8Kd4JLXmwpMK}FCv=i z`r6?8J3Afqu=%OUCTGn=e|ISsETNYG6+ZID@}jZ+-U_YPGo!;kt`1p=F@rxlkB?8B zO=O3Af0`+9WyXZo7UV>ExvtJm<)(ex+}u3eKd8utj1LaPX>gfx6Fryqy!hw3p1Bz} z(KGJ|_dvu2Ka*#fpBUfT+M1u6Ut3?_-PzgR+*)5>UtC;VT3p&(S>D;+SzcM$+1;O? zo_^+shetbGTZ7XpuT!>}L9>{8&G1I&^p4qumGRD&^oC)>ltEc=U+3r&hg+$@ z=W-gmd`bUmk?`Kd*;!REVs?5aE-rC;WVpS({p9@O!`DWY7=&_c@9E#`uB^D87|#;# z51o+6$lw6~7^qTAuR?6!ujbkxwr>mE&1S~OoI@Zr4$AHAouQ$jVPRpxK|$g%!#=(N z>26jPWfd_|kr5d%L>FRr?;s*P0t5nufkL7J{7OG+Cq{;L{pj!u1vkUtKYnyH*41V~ z(t?A7l{_*>M@A4WP0#hV)l^Lm4}Oi0o0^)Q8XN0JbhTF$!K*8Zp&8xXJ>5UM`#am8 zBa2%{JU)A;C4XyfXlSV_?e6ZbEGgRG+b_+_T^Q&dZmfs{NB?SqwG`(RxtZpFvMwtw zs;;hvLZO4d1}7$`F#Lsvo_GI~m!6hBfQXV!T+K8ruW#|_7U}#ItNwX!oIES?=|AwF z=GZifMDF}I{~vf|%BqG8N)cK6AKw3yalhkpIo6O>ghX0>!wga-w(dtR9m5Nr9{5iG z_xTK(-YLj8-g)Iin;SP>REC@X3421?_~fqthasQZmj?ceJU-2>9-JeQN2gcM_Hown zGtU0k$TMtZJVW=2JhH{__vG}()YezgQ2>BKQdL3Lz!x|(9u4eezyipS5)&yTKc+td zr`kha(n!2gD1vEcE5?z{j+9?Vyg|@onMgXLxSzQ}Gi~z{;E+EWEtQv45Qt`?khxJ?RrC_3`Iv8-1r6sDWWKZcJ!raV|IP zv_BGh-O@JRSp?5yqC-#_ab=ESR5Zj+H`3c$@5)m;A1N3@UWeg!X{3KHHG9|m6yh>* zfd83q_e2Vyla0b6|Ho9^KDJ9EgpC*w$e(5^Tvly&^<6!l@=NaYyIZPP7xRCLq6JY3k8e3<kLaq3*Zqdm8T(FUobq=^OFb|97t={zCES$vy4P+B!VOM{ooL4JQe zfBS7CJVP9>Ap&LJt;`gp68!Hjj1RjHo!a{kFDQujjoMtzwZbiklHIqIOrmgNi1|~% z&&-QkuEy%PAUWXKE4NiGx@H7LOq}%>ZjL&Nirq!jRV=H3SAn!<&1HTcf!XboOB=Rk znSKaull_$M$5hK&wt)`=kCp@6Ou^xljV-_OETEIBAAItq-O^OmZdt%#Z}kr}FLLa% zs`pM4JYuXO!o+nqTFWdqDejp2xf)+8L-?u!(*3ORR^;JKaZAZuO{+LHj2bxGeA5@( zHA|MR7I49`@<2?7tQS+@`XeCeb{{A`TmRyIvt=lZ97ThTWLHc_R zT4H)0vVEfxr**=#Cja!YIYk#zxbkqLJ3)?LLHr^^8Sj8KHoT`WT%j&!9>v{{TlKA! z*jr7~;}4)_8#6bpx$B3m>l1Mw%vSFG1%@Rxz0=j!pnqEu1Du!Cj7a+B_%l{^f-!e1 z3yT#~O=LUdcsmr8ZcR1ZO*$((A9~avI8vcWvdSTn0>ms2TmI_dMk3``Kq{fBXC!tY z_rrwcqrA~SN>8-1EvbMw(2n10Yl^ol7=3~#_u)zhgRN>OTP6t;mYY}|z7+)C&S-_}J67@*wa;wWV5jEz@;A+KUx}|!ILO1*3r>|Fi((wyd-a%{q9KqVSBOi5 zS9Crd*Gctjc4zZdtl0BB>D1cw9wyVCUKA`1ix;>uzBl3l0jvLreqg(QZs?kAUk7SL z3b@#Y*{3e?R)54>zP<8lq zCC?U9wrZURroiTXuW2KvIumJ_ADBT2&*S-BuVD@6qC9U<>K@SnjeE19J2Y!#X#((c zf9RO$YLD-=31DF-;x|X zqy0IFpUncB^2COvB!+&E1CLSNR0Z1UB?)3rNLKWwOs!RefMM5hVMR8w z5HCuqy>3+-fj-_hHJg@Jfw2~K8CMEBR&_g|qes{$1wQKHHJ$VEUF@CT>G=Q7yh~+> zz}nF83ofq8FffN&(AnDFD1Ub#LgPRqRQsCWrd>7_#A8R}|AW0sfN3OP1dI;I^vKy( z#s*Xj@tq4uvsKEAD;_v{xHbby-hBqLZ8fiO!2rn~L$vvpQ-2H?Ncn&H<$$eu5XAKS zmdp>ySg(}lnfuAzbcj`Th@JE)O-x3>WKuKk=uGG`iW$f6S>k3?VzZZCgM3~%^aJRh zk3rAvdKwRp3cCcWwS)osuPYvfE%DAFTs_f30%);Se}xy}!+mw%tsib17dfD%12=?l z-*KDE7rMl2CmD?DY%#;`5^kxkIY!zPM4PAr_5ekzn0{!rTaPw!LOLcd0I^U=(coHT(YGd!l*2W|zWph4-7+1!q=*Xht(+U(o1g zVp|r@O$@kqJzUt2{3T}t zWr)b^H*;__QOnMVpsF@m^xDV`n6w%=k1`i=@20~#%r+XGSgVLT6!9s_kFpONONeuh znDd~lVoMN0k_BN{ ztH@R{J2pDyhWfYov@KVD(0mvEcSxZ3yhUq3vs-QB@7@3sKXMM-waMW0QA4j%U60cF z>}bxI^Q)&yWJ7i$6hpZ2?%g+B^a1C-OdY|`g=>~IGe^M$+LSG(gaFU&lBjiuDsq@v zHY(mv0eL{I+bafH?`SA01bBO1@#XLC&h<&D$o0@rWwTPsVgV)^_gEx@o>R=!y#|mn z^k^)*Y-0Ir^0B5r;Z|~A%N-Oxdg^=P)wV)+raZ_k!=FuEB~s`TN@7e6_wZfs`?O<8ie1*Y^#e;zV_=jQbhF}r&L*`D zo=a0%Km|fa&u*HlgOmP3lLu3Ia}qtg`3;1___ts79M4%;7WDR_6Y{U19GsFdHMnj3 zcIKV4-ykPt8%w)v=B0y92tTNm zao!isj0Uqb5jTnNL5bxO8V_JQi4PbTAwRjgCX5=pa~^@Vk}Ia<>+5rx zBXWtsWnQ{=2QDt(?!>)CS-8)ihz?*B{Zxi(-QSRY{kxNW5_#W5!m3SG;>a^1nBbQk z7-|ijOy$rGcHNQs9BP-L(567Ve)Li*F&GvHh*Ltt* zZ`NL>tO71rwP~b+3S%yCN!7_@aH)EPQQLC^cR~C2d!oK>#_PAN`x!l*!d=6tZ00OW098t+st;1uMoak`wA1s1Elsvw$cLAB9i+;$;UH{?&Zm&12kdIB%pZRmIULBr!J7dbM{Yq8z zE};|4!UZ5i7nW>fW?V>; zKJSMC9-Lp-w4<@HXeg{QycoUBY=@$pUoD<|G-^r5x~|ClR@%nsYkG%4IMURR#6^_X zr-kSM3Lw^T3`@RG)iR5LCf?}e{fQ2P39jR9v~pL`SbIdHC%Y`+m`!*s!^#OTmv3-1 zD`R^@>%bmct|5T-ud`8D_=Rn>Q8xE+gxu~}iQn;T-Do>@%e^Z3jk>u3gW%5h#y4&L z&ODTe{lcc=e3nXKv@|}og+cx8C(ctzPp(XrnZxK0-arQ_8kft!2WT9#S^y@CF1_m` zhnylbxZvf_tc`tBDL(G^1%2IICH@t;lsDg#Zer1tk6hIYN0?V`@y2AwT`PsauF2na z%gW=(0gB%gk|l@dA>xzDsEa|bPUXl*vSA{wGtvZ)s+`fsv~l28#I1bmwsyd~ai`jo ze*(W|X-8+x0a@XpmX?C9qafQ7_y+$Dp!krbWMUjt^S@b@@)h~(oKX@^g*4->Q{`<~i!@2{C< zsqgDx#76>?vFz5I4rh^%-eYr7VfV-$0`Y`2&S+(0*agpiVgx4TzLe!}U zn3JD0Io!Fsi6j9M5=X;*B|QQB3f)XE-JkQg5Ku{&FX8&WtC77X26)<_fC-)M#GCp zWe*89IFEte#Vwv^beA(lBVvCFpCna3X z7P^HAdfKQx$_qwrG45+-F4n02u#gN*XufbzH7_G)y(b7eBU==TRrgS^6FQkAFuLwI z-upcyx&9k(BS1jFvQ@v9=H0872oohHqss*io9oDT+NUdQjJEoUX)#08IKRW6%FKvV z6K%F?%d5>Y4y|hU_Od>ZzGGr^gifaoWkN%2;Mz9^tmqr2Hehf8x16<@7=|U$(czc+`c8&ST8a|v>Mv>!q*X`VVr*XQQdi5uyo-UcEaw}_L zVKXQ!`^L7H+wY45?Cj|vAI^yaW@B#}aC)(H(ltArtNn&$*9^c#)ISy0%vionNq}I1 zIWT2O;u1IbVnjhIg;&*7W;4(JqMmo~7?-F*oYd>mYyJo=a&sQ)x1w?bbWupHhZ6^d zUlmFK;0wY@fMIGS8dL$@3VJ6PR|fIW4f^?JYa*xVM4dieeSy)*eNGG2u$vaLy4J+R zBGCQ~uZ{bFtSDCYH#TuO=`wC|wnJ;t5^*uhskpUGMW@IC_1t+f!nQ#9D$H_REyD$e@D(k0a*X!PqNhUq#pT zUhMr-$mb9FB+7*S9+oBKHOfb)u2rX}{*0Dqi`nY}8wwLDt8r1IW5t<|YEmwVL-j5m zQ7nfpCm8+-`N$OSyM%w)BG47EM!+|LMSnV20uqDIn!%|t3xl#(Wg&KlP9mYTM;ljE zu|SQ^P1N-6J27Jm?a5op&*qS(D1nO^L0bbs}DE^w|oyNKgX%O!QnKbpHn3~ z1lex%`mq(G3UNTRrApT{R@ISW@|f)BFEB~qCvVq1E-(EWv;*#}W2tL<1M?ML^e`OB znBp;K7yxmjF}!LKL=lw$G_z*=0OUzie0~5YY5Lb}t)<-I^--Vs(at^N74bAz>vs8W z;0q@d$oqFR?@n4!9|lFEgL1X{N5?qomm#&m$Vor zid_FFj?%^WorJ>>3MvTKP*!dN%D~^gL>w~6N0G7>Qsi1dnvW!S_2rE9+jvUybJCl| zuFKF2q!G+CY$oLl39&6XlZvtjIlHES>x8z`XeTlBkXKhYZ%%ofZploWe@FhwT(_J-)F;s5$HBp5 zb(+x$AAi`%9lw(LD^CHy_-6;5$MGH(#*k_+Qx7mk0Z{-Nxm(+OKHQ=xoYKRI#*vS2FT^+eeizxrj>C&SKswF=m&VWENkB`-zMM>FS2mKpH}fJLUUX0&qU S!t<8`Kvhvo0VZc1_WuB}nB+SE literal 0 HcmV?d00001 diff --git a/src/main/webapp/content/images/jhipster_family_member_1_head-256.png b/src/main/webapp/content/images/jhipster_family_member_1_head-256.png new file mode 100644 index 0000000000000000000000000000000000000000..443822e8ea1f8db01adc11f33e8685fb14b997dc GIT binary patch literal 9505 zcmZ{KWl$Wz((Ufz5(oqh1W3>j91eX0 zzPj(%dtE)%r%%=C>K`*xQ!^8)tSIvW`!zNI0C@3PR#FuJ06s$?02Ado*pz9h0RW&! zWd${<=hR6>+SoZ*K}_tkl$?^ZjD+l01to21Idy3{4QY9eXCNc5A@e`@8AyIsm-?*s zOiIeCgQeAk#Z`F0DoS6})a8^6l$7nh%E(G72W!cs=}VOuh*TJfRGUiFn2ObziXqH| znk^)&%p{-FXRkFCE7upUF%>VjQmD6(Y_*ntCi8XqGc`Y@eB}z4|KKOh=pssE%SU1M zk?gzR8&7fCU&`zToJ5soQo8JfTC4=>AMl?gN{qNLbLna`iE0BuxR+sXl*?#F=zMK{ zdw@-|lV*e6m$A~+vz?89*Z+`6U_5J3BhPynV*UCIY=Z(rvZ-YW@WK`b0s!yuBg5zJ5>$#Lho~H+<|% zP(!$fQ|eFC$nfyNfx+^kf{@^#SSRapb6G!MUnmsn1c@~C$tR=VHw6wH3Sh<^r$Kl~I3gm=JZa ze;6GVRaRQMyR(}b9X>fcxWBjGSzn!-9M@GtJu+*V`A*mR|6@0FX|qieCZPM6a3oe#ULDu0EEE2D+5z<%|1Moezpi#|@Zqi|=Yb-pXzz z>ih&1)S?}{P4V*Js0#G&u3S1KLBg_a+p7}}Q@H+C zmUpzfdin{e5B}!dwQcc({vvHq#-Vm)w#?jWJfXE zi-gM~9AsAi8imMWG3a`#IUb z^%T1KGY(*c9~La3fmzL}4;{uh;_VTLCKHeN_?K8sM)DL-`^5m8 zZ-(Q;q8S?ASK|Urp5pdHjw6?Q_8!AOZT~XESh(b)eUtZv%R67bZ1rZu5y$#;2juX( zs{p);Lxqp{)jAB}6pTfVt=-&2;(v@IGYi9Z)C=B~4|spd!`IgY5{XUoB67Lk#4cO7i$;e3TXtnC_I4+-AIPrpR(5?_ z={hZX%YV+KHfX03+&-lb!kPC80*+zWAq*N1YI37KZNthVpjMJJ1btJ)gfzYw|V!KbGh$i#DEala0ak% zMdxu_9;QYUqZknR6vu#dS>NtH@&k0{{zS?wE1x!B-4 zo9p)0o0L&oQAx1%-}NoK)(xc}%g^7{7pwX^Vx6+DrIPu9ZE3JKh5Q@aNReJ)SkH|8 zBN*D4A&S|-q!aBG!F5uM80O4- zHRi9rG+G&2mVzb}Gt7yi$PYAWplv7S8Z}EahBwg(WQA&=Oz}DITVu)Xc`Xw%I_)g#6 z4@TTZRD6>19Wr|)ntQ7Uyk-j9WFmK@I3diX3wxI-@q$EJCC6E=<8M%SG64DmQn&s@U=wgceXP;oZ4&-V#_{x;Mb5=#9WGqOv>%<_Z^{K+ zJ`J0jb`{GhT|>Rb$BT}_i&paayQcIHKZ8=3pHIY2wxhu$c7ii%XKlZRDLp+?!Dsv? zx+Emrf}6wL_We|n*mt!uzJOsxeQm7d6N(>ocCi;(E)ToiZ$>{wBXhJq3YQIoM^%fG z5L?=n(viIiMF$3dfOno*6BVh$fje4*&`RU@YVt>7FmQOkDy^^retky~cuw@2l60W^ zu%cCax?>!bN0&tGZhK5j#pCe&;O^j@^-rF2^>%!twq%QArNaDsO92wG#h~>T_is(! zo)?N7ALLGpLQ0D{%}6+Py=z}g;q5N}n{s0tos#C5=RqkrZG-4adnw1`S>ubR%jC@< zR}$eb6vFcHD+Wj}zo?IMbEF6rzZ22EL! z-ag*8u;^LeD_slbovmkYQLj}5MNnP;+5N+fPW!iJOvs;su|&b=?_VR=;OoWQ>)LUJ z?D^P?O048#`*B6Yma^#L$JBIE;y(lonSdT}b)v6{WkEW|s7(*MUJe#aqI?$dS8B7g zIQ7jJ2wU<3vHuISeAI~S4M|+5LX6$PelM0aQht8Dg0&)-jJakKUl5gUe$t?@c3%JY)JqU-sos<+9!e+Z?)-FtD()FpDz{5^@mC0t=|G@A1U)bMR(aI;WZ2`*J!Goc}Y9EA=&NSoepsnC_3 z%;-d{l(MS^Cm#d#wC7d41fJcGp9(1g7~1hg>fnG7l|^oVNkIib63)ehu1aj_nZw+T z{~rE1ESk=oswN~q1grmF$Ym)xvrNk}8$hC*)z`_TgR*j(wvzq(y!Dt@cuXAa&KMPE zd`Z_HF;sw%pEC#7E%h7pfXAiY68>cbkKQ{)0(=gPBFkbseMVsH3wCA zFVf)G!seIZ1^^r117XQ{N6qYFb;yAee5eLvX{h1Y&7T7cWwq{>-Om5)L#fHQI>TC0 z4Tv&XP;RO^R&{55Ey=3^%q!JZ!8-7yqX9;xz~q$OP}mryD;Ni3T5hc`@z%ULW8Ek) zi{WOEu|88Z*Zys;_4xjbEP0+_#M?^cdiQi77T1o0V3|V$CDEPoCPQ!+ZJrB_EO>}$ zu+=M8?;K03@7v^%YjERho7ZS-n92diGR2a_6iq$!F+QL>gSWpmf$K5{`HAyII2-oX zO}W<3XCw;>Jn@j#r|F!s3t?ueJuR9_B791`*C(idxiD^HrZZ4>;P=&13$sP0Af8Yb z3{uYsilknSUNrKBL*$OU-c5-~?Den`HIeGq&hLr&v8M=TmAh4?55g2ZR*BvA!-2B< zp5b+PIo}B#cRZGxm8Q}uky(_dnH{FLZB6z`?XNk(+cJm9t)v_nxvQb#POrcPyqQxETn!|~H)cfuU$2qDhwHW=f zp3onjE<~UdY-7B&)<2R_UmB7p(G**2}veED^3H>5-v9Jzi8e+ekRt!mHo(I1VO(gQK zq`|0J-uO&8)E4NXcnwj3S(Kch2_<8+<5%BMcRQVkIil~yeC{S)@&!!j6bwQ6RnRZ`%_;(L7j4LAQmkcw940wa>M3@Xxs%r%hmomd%r?ItY4z0y z>2Vh^6?_1JNuRi7iufxiPk37deMf;*Ux@NyJBwB5f-XwYG%FVWTUvTO1Vb%;;vgcP z89H>593I1M65$1cTA3Kd;v3uMJ{H|wX{r5TZ#>74)=VZ1fQYrk>wh?X9_jvrz6aA%BVm_F5kdR{uxK1Yeor7t`RWm{-Df% zgHL^y&fcgmF29xXAC!(WW+E!+EUw5fRZg94_nTH;@m~F)Kek^i3pZ?(`{6TLE_(R% zU1Y;AwP0Lx?I+7 z&DmBJD}r^huOo9u2K6Kq24n&`a$z{VTn9C`el0$dA)TkvyWCvf#6sdZ_QBuKvfIH{ zKK7S0}J zOYBRMb>Z^?EaL#cry1c`dP-r*Xq7apofd& zI9GUzy$4lb_(~4&^3Q-APyhWC{U^xYfACX@A!av2W838N?<-!9?(J4aYR@Anj&xFy z>&oTAP8p?ik)PL*nJrIuht;}21$lWP#!Mu$KDQ4Y@4$xW3kcL-l8^?BlZxr2M28nz@<5LyIRdT!^5BI*k{7 ze19W3NZVXokn@v;`g5NJ(tP#`lU5(Ji4URx9InQD=##GnDq+~2S#MAP=>GM+7~*EP z6OA|1r0sOc|k0I$H`d+n5;{FgZDvxR~g{(W1HfX z+ArGs3)1XDA1%raMlw^fyanCU9!`aP=G}aG=+JtbTJxA-u>Hn6oe*1qk!N0L z3~7i9eT7xXIgqikd7qzXxQRg#4 zo1N^i>~1>#PNFgJt>MUD#+<}ojU(78oOQ* z%2Gg-BF?ZMX$?*Tq7?%ncfMcN#ROIBDKMi!&A;JrR&_`qJY9{$_6>go7)G!MG6z52 z4De2%wnYXSf3Vr7$7QcUX&xiugRuU2ig~^mNW_TlkHt)^(#Fm7*)gYBUkZgN^!kbb z9?I!Tr1o&i^RvLYl}5~OB0DuYD-moP2vSF^Dm_*z_iS{bD-m zOF`VRrIl26c;_vHNd?&nsxy2~585rYi7bt*t&J>wL2I)Xr?JvrBq1uRBl)o7Ak~~Z zpZDe08TF`*XC7|?XCBs8WanhB-|B5TWA8`w=%h{KM94n{BFdb8@Id_H2~>$ZSq-ErjCicDzeXwMyWs-G6h0RyXNV9DyYDtqI3v$B@XJa=4FZ<%@26G zW_#ZxRH_q3GP#b7`{Y8WQt%R0C^K31-lwrm8hgF1nOZu5F#8$^dyM^qq2 zV23gMq1oq?n$?id9;=t9%i?RHtAL{fYFX}UI|H3A=&q_x0oCMND?0EXL+$}4NIyRNy@tlq9%M(p@9)(j4duK-o(HB=kx z;Aqep-6u>_{&+q=E-qhFRXIk7#`B?}p~Vafi4@s(-}PCobP{VgQ5Vo{H!$xzLMfe7 z5)zI4!Tfk?sAKj+GBt&K&{nuac-~6zAL(yLJJf-AAlA_rz~u_rwlW(iQ<(6KC_+Lz z-?leTIMx{CYV*eT{X)A|TK__GKSc{;wZ14o@-_O33**UP;HQS+@DFSJE(986mf5Iw zzRg(BrjI=V6VBgl=w6a>FOG{*>g=TkxXz;VGr|(OjlTmF3)~@7GSo+lX!DDzdyx9Y zgs%7aA8CEJl89cHzrdL(n4HShw!{DW>HE;@b<6^K3JxFq3zy8AQ#m33S)$}_xAcNH z7;CXkc2BGF$H((u>+`iB^+jRl9uz500vzSyGXCV|ROZRQpfo)F4ND>2&2Y*C5@}x! z^mgT^2|KJpN75lBC4n&#g!QW?G+F<5e8f8rEgv^tGBlylQLxh)Z&*l!=~TW#8AB=B zyQvF-0HbzG@$+T*%R5XqP3g!rNtt}CNt!p)0dT0qC5DOQ-)1Iqwh=QZdzLb~+i;Y( zNG$QefLgQzC%R?0%=mBFAG(GWkoWV2V1?%jaxp0__BgV(@hLb&dZ`ojnHe)L(Ivkz zVcmf213FU@xk3$#h!#fNewD2Wjn=G@fnt~8x)qdPe$L?;wM*V|hZ;M=s$kJCO(lr{ z6_gE*79VgESoddMt?}%=dc;(~Dzoon4RU;EK;x`TIR8&@!Q)qmEr_=|ww2&28Le1N z6$k}8ZVgIcVU8sTpmxedS(GWdmzaL2E3s)}n6Ek^Cd*)Wle90FHq@k2e$h}c*R=Ki zcezc;w~_rf8C+rE-NoMCX&;?2s)uOi1i=kVOd2Rp1@^!v%T+w#!e8Le80j zi*7}cGi=0J`{E~r`dTJ>OBgE#IZkJ>?cR3~{jW>V&7+*3@zHlTR}_&@3$ilPNmb6v zC=KQ|WFN+_uaJjK-D*Qi_}>gQn=%AZEfn_)%Q=phmYwb3U;daKQU6fpa48>OJ7$d* zAWr^k^gJm75?HuTJnc)I{(-6wiyy@%W2A%!ICK-_%)oT<0ceArSU}6I9LtvF@0@F- z@gz~B|4Lo0PWH*QDtWs(W!R;CvV8%DX1Gc0a(qH+O-Epwg{t{8Tue*c@y$egsXvA#cB0oQ^ zTky35ATx3j6QE9#yS^L&xa|EdQT2%iVC4c9K5TA5E5rC&N{NO%o+`daKCC9JivgyZ zI9{K2qmhkevS;ZcdLy*jN9zF3%T~7$ z?T5>ebQ0n7#%x`R_RZE1Y7KLzVAN)JxUX#dW3z%h$u!}S`1d`uH2ne90^Bi^kaBDn zZbL-Y4lWlCA_Es=E?01PXT=0^_oPUbEbRSMr`y<0*vX`FJt~Js-lWATNd|-Z*ZR&Q z3)nGdRM274bz2tc5n^XjS&$@c008$z6v!AC(MaWK{l(m1dvum+em(bZrbHrbl(+JL zj-v%BlpU(>F)Tjg>c2AdM4uQu--gKf1Rb%j!ngx9q#k|-Xzf$bny8?baj0GYsk_z~ zIk{P1@BD7U(L%)Q$GkK@%yH@lBP7CwSlta3pH~{Pt{82o@-$ zp4zg4c|C_B9NKRP!~)8$qpp()U#q1>4y@!Z+rZwzHqSdOl7a{@Q1fvPn`j0m!&lsE7v+jWVBqy+>dj_rYh)bhIT}4npi!$zxHx*nb2-K%;}6kx|X*HOgxs5p?}e0 zhek79+ueOQ58J%W*BjNOiZZ}~?qrIs_u)>aaeivfA5ad9iD-NtEpI{*Nd zy~sr{v7e)LA4X&uu)I%P38(A9eKk0Zf#LU|k1X9baG)+^X#;L-=dj&Ty!=?xIGSOn zR`IF49N5?dxT#1cni=Oua{&n*S`$B^Wvpn0m**H=70T<7!AokUp#Lf~W)UCN-8?S? z^-%?rHNeJiYPwAjCOk7GDBcK9SIzb6AD{@j{-pv^%O{^%1NDnehe!7o4t2!f)6LY4 zTBDHqi8ndBQWQmQ;CYuv3Wackh<#*vr-7oKfja!u$HZ8iQj`xa^3nOs$7YE=e(=n1 z7=lPr32U@d7NZnNdQ&TnV@v)(8xf7Y>26!=N$KXBRp+rI;kNytxteats_0mEY!~(d ziP0)Hcgk&~nFSD(OoUzau8=NL5Qj3|pEfDP)2d0=pPqc1X4%7E(m}FYQaXg0X5B@A zdM|95GHh< zkZ^#j3i1=b0=d$(QTMkh61qw(32H*uKORbNd^pp9qvLOZzR-2v9#f!DQ)zXf(jN() zsimov#=LGSWzT_J#5#5D;|}p^yM6+9JEpm8oL1z!t(UqVu~l_#JNpU4T9~5gOHlk_ zD(>x1a_pd2@=gZ6E$KB0V{1Q|f-cNNU8*O}g`z?1+C*n7jn3OK5Dstn+zV!-m+m3f z%4mU9*C;>+NsYV>x>~29qu?-d%n-dAeiZ;iwJ7d9<&o7I<>TzzYCh4)T~FV-S+Dez zk!kB&#_E5{#x}27wNn!VLg_!zniKP?E)b$Mi7{3Bq6H>p6_lQ}=l$M`zSR=;!duQ! zHAvk?lP!igB67hH$f0vJN9+Ce_LX>NlqN|{6pXr2SOw@A!*Ty2Fj5?SY@a@|D83;S z%jzsciS=cv%xv+L`F%`-uuZ*y!D8e;kp9JT@k(VIt82K^ur)nrRT)?fjBfB##7=1LiPtKON zjRjP9962!8?PM{jHSl79JQRfYu}~k54)shFgomDlPCqt{dQyy#k~|5d3O-RtV2~6j zfzw!@RP{$OKtnD7R$^=Vj8{WX# z2mq&9{2)2^TA#Gnqg_Hbh(#rfEC=L4@MU9E^oqI^M2qjubgu2yRM^XqI>urT~{p3ID(d@9n+EEEB?+w`&LVhmuoQ*YH>Jf-|P*J zZR29;58g@DmJAd%*);hSn$h!)(H?B*E%crtZtBI45!Zy#54Ma%i^6x62XimL<+=~T zx_uVj0kZk3*mA^vAW6{9yqg}|jmqzJf{5u(llF!A)^2_)DV!2g;;~jSYWk6F4NbGg zhmyJTR-&`KNJWdbN%F7cvb}u!Fix+j>bNAvQ5Pi=C7BPR9i0^SN-wrg?(X6k1qB5& zy%3JOihdGwS!@|XZF42vWaG;D_4Yz5yEkXTPoG(%qLJH2Z{|8dGz_I`U literal 0 HcmV?d00001 diff --git a/src/main/webapp/content/images/jhipster_family_member_1_head-384.png b/src/main/webapp/content/images/jhipster_family_member_1_head-384.png new file mode 100644 index 0000000000000000000000000000000000000000..4a5e9fe4775b79347fcf33c97a7ad10e3f693b2c GIT binary patch literal 15054 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 O0c0c;#cM>30{;*Cash(? literal 0 HcmV?d00001 diff --git a/src/main/webapp/content/images/jhipster_family_member_1_head-512.png b/src/main/webapp/content/images/jhipster_family_member_1_head-512.png new file mode 100644 index 0000000000000000000000000000000000000000..66c625c10626011b5e142ce32ca009cbaea5f0bb GIT binary patch literal 16456 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 literal 0 HcmV?d00001 diff --git a/src/main/webapp/content/images/jhipster_family_member_2.svg b/src/main/webapp/content/images/jhipster_family_member_2.svg new file mode 100644 index 00000000..51c6a5a9 --- /dev/null +++ b/src/main/webapp/content/images/jhipster_family_member_2.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/content/images/jhipster_family_member_2_head-192.png b/src/main/webapp/content/images/jhipster_family_member_2_head-192.png new file mode 100644 index 0000000000000000000000000000000000000000..24baf78ce8326c3c2756f10ebed90829d0bddf30 GIT binary patch literal 5423 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 X%hVz6s@WHxK!C2Mp+=25A@Y9!nF9~R literal 0 HcmV?d00001 diff --git a/src/main/webapp/content/images/jhipster_family_member_2_head-256.png b/src/main/webapp/content/images/jhipster_family_member_2_head-256.png new file mode 100644 index 0000000000000000000000000000000000000000..7b25f52f93d109da2df18f8442ef2104a6e14b8f GIT binary patch literal 6687 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 y!w`2PZFekY^= literal 0 HcmV?d00001 diff --git a/src/main/webapp/content/images/jhipster_family_member_2_head-384.png b/src/main/webapp/content/images/jhipster_family_member_2_head-384.png new file mode 100644 index 0000000000000000000000000000000000000000..e89e120c2f015ccb140678ffff8ccbacb1ae1f20 GIT binary patch literal 9682 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 V^p!DYUi_f~=xZBm)oD1z{4Y|2cgz3) literal 0 HcmV?d00001 diff --git a/src/main/webapp/content/images/jhipster_family_member_2_head-512.png b/src/main/webapp/content/images/jhipster_family_member_2_head-512.png new file mode 100644 index 0000000000000000000000000000000000000000..3c0e6cb9af31e2e46d9e9b6c8cec99c0d4c5e5c6 GIT binary patch literal 10514 zcma)icQjnl_wT(^jBb?ZWr*m#ccXVk?+givUV|jU=pqs|f&@blJwy^E#1Ji_1yLdx z(nLh>!93sJTfbM)rdDz1CT0-E$9PZa_oHP6+@2jgg_Q6#zgkAOs+X zUJUOm`3EnUKNwRRy$e=OUQI&>qp5?@F|bxen_rN+w&ew>X<^j0EHrg2(YlrwS_>r| zw4SAgj+M5)^#xFrQ$y=n>FHwBv@BFL|I0;1M_o<-La3#0tz&3oU}C3lV)vi7v7Ldj zts2@wMZ;7{%}_zbKwc3oEu$SU|u`hW);i#1&R* z9Wj>I;i>@!SEf?+qs)bKvC%fQAMLG8t3*0sets8Ui+|4MGcDj2-R^!4;+{{Zexm)XA zzhP?kbiSvYPo7!YJH_A%4o1q{tc>owU`{3qK{eA0mQP4jSl5YziE^*Ppb6ECy_{J8_UO|)%!Jm2kl8GlTD{TC(log{w)n$B>Ahr_9EJU zB}sk(R##UK4h|9%6Wv^$OG--KzI_`O5j8V2)8F53VPVnK)RY%}^|p`G)!00XkOwB- zkNCu7cM=W$nuxE<^REkVZ+I&0KeF5i)YyG!y2i@7bz67UNr9D~f`$UlNP{>ou{*7F zeif-j%(2vCrdu(Rm}F)SWJb=Qc;`e>izTG12yOa#sjRWQ)n# zDrsYtwA%Ot5+vj!WK_*GO(*!TY{&_G;NfkNRDY&ul5G(fc{R=7=aEM+-Z8Qk=Xe+E z6sK;2UF1WRh)TF<8db6|VU+bEG<5t`b;2w{WW#Dw^*x%E3?;E?cwLu^p1Gi55qY`G ze1)>or7H4|(CRnT^|Dn}a^w|bv~=UqS}8hOSy~zo(3*cmc{kOCx6q;+3IdzzA{(+- zwzNezHAFfSv+)=0ejILPZVj+wl52;T4oRPzvb>eZ#7QN$8FgKPy4Lwh(~k{14+TnNrcq`yx|r1pZCL*4!nVolrdhGXHUo|U^z&3_TQ0KmUs zq^o5U1+LF0b=OHDVZ`q2?u7=DM#)>M<-C$(r__dJue25Zk*9*cUEOkR7l;;eG~Z9Y z@c(LQuj5&X=zZK@RsTH7E%~M7^l(5ie@h2z#Nf>lw%E}iiLsYrk%7CXZ8zgxCUbX@ zepGG6(myPtC(sg=5o#D_EeWp!wJAFtiSm_C(bR3}ZQr(;2laZnbPQ3C$8RTpQf#!~ zR@QOkk+QH#&e?30fCH3>Ak+@^kUjDvWpW;Ry^w8DMLRuBp_uR)`Gm5#M>6xR1~5c1 z_c}<;?2S&kk4*947}D9QxHc9|Z4~vUBgxVj6lKTI5ros|L%;0{E+qcSnca142gd|* zelgb5f3N&S>I{hg8g{l%edBD&geP;pp(}ad)p@xeZ_5d()_n^1Ws;UhXY_Mmc?52H ziCR-@v^nTi|4e4{`ojZ%@$Z~jmtR9S{DysU=IM*&MC(K8wR0-*uE)?gBjb{Pjomv8Ly!C!#1sL?!s#R6i@l(-<#!E z4?+iUQg5&SmSs=s{-d~HTZc3J(*AKKm}`#jDQo@oYjNVg93CmtD;uzlF1Ss7U)5q! zmE(KaEu*`Xn~!UQ@)9@EQ|miyvtm`69?`tVW=WfTnzsuV*XuDtZmj|bu)eVjeBKdH z-qeeHps%>{XiiOLS@TR=o#IDwfpz7!hSN(IQ3p%Sov zCSCrql|5JKF1(E#3aA{@czlh-L9Te^CX2xFj^<5jhZTyO%tH&LdA|TadP}9Op+_yO zL_L*t*2>;PGrd-m5$Zl==0IC_D&O>u4`nL*Yzd>%O?Q(%&9(AWM;U@x<4aG;IWN#} z#MAbGQp=?b(8?({A@Cco^*W`mc(nGdSy-5_IA?B(`N5P^)Gubw*rz-k5mp zK|z?M?>o<7r$PWd#ehrA1C~xm5R`}EWot-FG~Qv zO6=CHH~wA#ibo5nJ#bB0D1LffbXf|4UC&%u&FOJmat7|HYrhvjfTidQpJE`cUiDr6o2^6^c9QOKBQhMmUw8l;HSr#U9=MJb;gbBvkTB-U-TW?Ns z>flOJj&~LLzly36;ioTWL8$|3X}HU6^4wWj9W~_W{Ii~h3J2~-iu!%CiYN{0L1ldQ ztfF2A^XO53R$~x@D1YqLUmA^`DIP+&!cfZ2$njjJkGALk+&Kf3N)!H?-aBr0-h%$V zl^uN@$}N5sVsmqH=(^J7l2>M~;vqC@4C|d|A0R<-HTrxa0d+SQCtErqrwj2&FKh7# zvh)}5JHNtt8S&Xw@i%k3=kS>6-4gICEcQrf-6g|~!=O{S*3vGl7%VA7z2zPYf!~mF zC=_LBEc!-$^os1IYrZvw|D)|!$i=FNhxVZA3XMY10IW({$w?@vv3Qc2b@M*0bHXAc z-ZC$*r^Z`C7V8>5E3qdUC-3CM_k8VKooE<7`^ti%Zam6K;r`rbAc`zHh5;1|uX^YQcgW%h5Rumjk?WpZOMEj%J+KCV>4=230=&URQ#kNSJeWUUDNPbw^;r#{U?!i5e^3U+R{@pAvykQ7F%r9{ zGrp2XYFqZOCVOgW{}d2_Y_kh)&;@%x$Xg1M$5A!(V$#Vvi|SA;6tz z8klSf{V#Xwj3#;P<48g(V3TZ|5CRfw@tyeqOoXm~pCC+PI^ruMJ%T^GP0a72+V%r7 zhZ3bhoalD!d@{?Al2|Ji9NW28@SZc^WDzS3O1-y$KZvD3|4lvjgb>*@*`pzvEx4hH zj#CaB2+ohtoZt@GB8RvM@q=-@xpK7rW=$UaV zghaMi0veXm=-(;cWI)4b@2aGm*A|h=#E2@fvU0`}d(8@Sea zF=Ra~Y`E1wLd)IRwI6%1-yPO+$THFaW4A69ciD1#X)OQU@HlN)p7Mna!=O7=lpI;` zz&K9hQ<{3P@ih;w`>7{Y4}2npwxxsZJ6ZP(oi^$6PJ2ZU2;5oL)%U_tbU`3b&8BYG z0W^XC3}|){QSRApu>K);Punp3xdwxkk`Epvm`h*ABgpb7s)m%~21>HMl)?lt_25-9 zBZny`|D_dmj9^*TJ$b7E8Sq$~h`B4`b{jhWYepr5doOXMDExFk*_SL@0hZ4Dpx8TK z8jr||(%aganVhO1el4CNLvilH@1P_zz;m*z#n2kP;F415pQ)5(`aD*nee9LRQN{4; zhGWHe8>*<%=waiolY&e)?yh9OKcPZoPun^N-g9!V3PYd_HvdDHpG`W@+_w@WBp)wi7z z#_t;GHeBcXrEBA;dTUWxKPT|<3wo=wm$sOgB+kkgf?Q5a`(}V+hDhP6(6MXbk1x>o zCgDqooLvEza+jKfWp~+(DMQA;?#M8IXCQgL zh~t3}HOBa|(})E3x-~p2>EhgZ2};(>*ivy0J^vDmo7bFcdvW$EO9=S(0qWoNasR+` zA)o{?__B5{kw_WBVc1>mD}j(owNX(zvz#$IxPfHz9`*M+|E>O}izmCX;)QpF*%)X3 zXzIwu`MbG=pkF^(3}5Asyz69A1?DQ>G&I&-EacFgGLwl<0#X+@HO7B+GW}O zS0DI&hFoyAVfu|WuF>Xjp?m=z9 z>qv82dfa|g5406WjyF^aq)7hE6)Gl3jp89aGUkf%P)3EY=>itR;K2=a1Cg)cn3Txv?+~OQ+{+bokzxc@AVTj|lNzFdC#XP5He3O1RHZPE zk~|kO2dE+CY(Fs)Ub2x*Akj3s(I0@P;>apqXlk@)a@;uRpMYT{3Q0O&l~>vIF<|I*Ij zE;FKOJlg~2-&h=;Ag!kv2wq`ysWf=nx^BmR1alsTVqE}iEL8u8p?8pX0laN5Dk}zz z2{$5~EWv6rsTu~bb-P^UZ!bjqLVIV>+6+uY&KS-iZWF3ZU_|iwRi3q7MeqpNC`GJa@Iu;!SV$9g$%{MbZUWOBsZ!xq41&vY;RghYbe0w)_w*s<-n`8|~jL_$3y~u?~jm7dBSg`8fq#*S+A|uBI!qmn*m~Q041|KZ9Y}^;5f~4S zjY1`ZhB!($A(+U-4@Hd%Y-wwvLV_AWH7NwF29|mL$gr56KTO-O-0Fdz?GCi(i7f?z z8ZL1antztSU*mr#X711>MOW%N>~HAVZ68tU7T+gsHcaQ~C|Rb~I2zo$AqM(hVGqcl zbwW1l3YQ~}Q3|=M-GbtdM>*PXkZ4Y+2MeC(WRzUyNPEl^Vk0(s z*W|OXEQ8v+YR5DMgQ#7m&H9S9az}b0kF2s0Mz^@K5mUz+?!rrP4DX663=7s%1!1|W zc`u)Pi=g`_MD#u|4K}W;^cgv?aX-0IYPI27LH>t|-{?*L^EK~H2l5tEAbWAqVpBw9 z@Y0}AM(C~{yfgf^*Kdo14LF=71qE}8TQbyd*WX{yocbF$!YOmj z``cpEd>QjvX|6pOyLYrNaONa_13EQhcC>d$<4#5Ona9B&iQLW4&~VF zx-&u(GLJheUKobEnz24|kI6LiwM9O(kvf$r{K72ggWGe%ZgR(Y0(WACdpVXJ#761U z?p?&qdhN~%YcjnD6AO-^O1E7N3&rd3W1p`7CHhO5v=|Z?AV_gwk`KH~4h%>rk6&Ss zGG#{MPX7_5NgzSKRQRy_<24W*?4m2)4Tf^i)Y_*AyejF!Ir9#Wn(=`G z5*PiCNVd%j93OTkd?yIPa6ZEG7i#2dx;$f+b{8b#K(t~=WsxsE1_fySBRUn3_PPxu zM`F`1#j&ku0W166BR!l)B|!B^mdNZy5>XbPPKXmvg|2y-3-=Z6XF{FH(Ty5WS_OVp z=o~bRDA@Nq!ONqef|<`0;=>4H3%ySFW7S>ntFoaGZp$v{ObrL$ux9PFE|JfB$`rk3 zrS2c@8HI?}V7zR7!&Ga0FSVR!Ve>enG5{$vrK2aLP!%G7X3R=d>n|;wLZrWs33_t$h85DEu_{J55p^x`TBm{^=@%1_R!2=?SKTZ7=gHYxtfh zVR9;lXm;b-UvA$-9+=HfyCLF}@s@yAq3qhReaU!Aw$nnxlk~?|=aRWdBCW5H9!wo6 zEzjoq^PMw4`mn7P8me)3>X?h&hf17tjhm&%zn>EjOm;{hm}tgcVYO?b@H+Y7Pv3(n zkxZt%a`|C=?5hPGjtw$!n7XQmbG-g%Jht#XD)l#GUl%V{YpAj=DM@qvYqvSyN0x!@ zZ&a-zzjV+{umB&eOT1jBS8$ERH1p=<6dXNJDct5Z-E-levDbfUEEZ8m2A;^c$XY*j zZ&uoOd|MmzI^eU9pQt7>{42oSbG4>^b}T{3>ATld&$sID%GiOyI%#lzq3@hM&s4U+?4>yM%rySNIW5i01IF zFKBcCtS4^pvjOhObL}2^RCojSeLW~qKfl7{+~WsHFZWr2KM>93{k*#jOgDPQxD2#1 zCC#N8i*XR*zaCeQC&orm_K3kO#5mC@Htfa-0?Q%q;u+iofy^1G}s-a@1k0wMY;>x$UlN+C%N&vEbp5^5<*V{kH9rs|c*7yQ_p<4pW>JKke2uRE{ftyr$oP)hCEdj)&XAZ>r z#0Y(z<75Ri^D$PngROw)gipcaZejf8($FH3&I%o*XhaGG9wv$?>ywQO7L7K7!S9lf zcIl7=HsMF_$p|0nxAy1+aJ)Y9a0DUphme}h)U{v$I^m0>C3eG>07Vmpb-x@#?gD45s>$lG1>)Va)J>Z2J%hH& z_Fst`EbqiYSF@Z1SAn$t(-=sc5!EzJFc4t%NA9sJ&~>E33RmrOxI;mU)w*rg4Z7st zjYp*&NX9h&%VG5UOVc&L1LZ73T-4BU*--Xh;&(_*+K-o%V$`l zxg2C3zOf+H3n<~jCj3BJe8}uhP(UMSYSculgyZ-o@JjfSKk%a5ftnWY<6^qrmD_Yt ze-phDPV`qw?2w>8;A&MzM~h$SI2C5}2c(4~$Y}BYF($9x{Hw!*Vn)rp2O6zEPJceO zOEOO^%Jhc%1BV-9=ec|`INs`mG#bKbS#ZG8b|N+2pyoj-$n;e)_KUm>PP+OHj%@4u zyW`2gOfcN&HLV=Z^qQ5OaQ~KtTvJzNIyKg)M!fW}du*fYXi{JhFgJY7 zCWp)Wwzy!rCkr~n zv*K$Rgf%n5#08kKGiAGke)3AS8;)rTUBd(I<%`Hm=x)k(GC1-dKvo49+hO2x46S=$ zM=501!3b^&T-U%!3*mac82h3j`PnmYc(9M&-FVoRzFT?$xsisGBV8)M{QZFxsOID$ zuM0@uk~IX3=8WG0utxq(CI<9}wAw*LrmEmhB@UqEsq4s&0-$_5ZXoC{Mo6g%2LkwZ zzU+WtTk>M-Rt*wLhHRBQwDB%XR%VO1 z{8wYBxvt`?h^b7oYos&HM~_4GHm19NtpmTDGN$cScl(|PfXS9(jJOu{*8%22z=xdu z&l_Un#Yp>^QvPj+^hffdt5|@8_*{c^aVC z`S=3nPN$8Io$<-Y7O%ztQ#vxzB~?geXfH0}Xm{tw*t1HY<Y7GhTMuG^_@+UvAAQT80wEXeb+Uq}7 z^F23_7)vTOP8qRSu@+i_nE#!fT4pS{1$vNuIGt~~=3=4?zvoidYn8Wu=DN8Dpc9Ye|4KAPEc5w)uwj}`CN zM?6n^MBt#k8>?W}GS}EYcSrm@1sWEj$vOMHi`&VQe5pL%JYsjWE5F`6DU3Uu<9xLC z^;N&@vl*i~SyUQK_R8&-Z@M!{hUz!h(jS+&!B}a{=Fj>y@4n&@%m<;a;2#eEr{c@M zVu_}f{`L!^*p)4NA8#>5gpc%mx(QAdw6PcRwrkoh9LCCQzs=Xfm z@higk-9e{sJKaWpKhID|iXe0(oLWF1iJB1HoN%M9;(l6N znQcX*$*t{1-u^lsYy;N;o)OBH?muc@RgHEz7-L>(K~HqX3TPRk$(MxU0M z8+xMTo}YrqZ;iiY5zIvhzr3L5=bm`tCP?3}MO0cH-`*VDku+d{mS$GIxs#q^eiMmy z2MmPMSjiVCLdg=dVbVw+5dVCoB`(Sz77Jrip}oBpEe;Phq=%MX>C?g6_Iss>kd;Z| zo|5L#U<`CvntxyE#^Xw0H3c_2B7!qWw@62km5Bqn6`bI#vzgDwbcv8l z0&~=c?f|@qRNEVxq=UYt9d5qCeO>H zyF{EfY|Z_M^T-sqoiwdaCRTgwH0OW zs|TClp+KU%mpXW{KB~=gko&H8p6*m+SYk7-!NxsS^D67!N}I2!A0>A(+q^ zUf8_b9gdg@7#{&pQy|vNdYX^=clHb}ey>GS!lCgUdp3r+A?s5^*ZgJ9gVwu;?A8s6o<^^kIb%s7CYf zqbuYYuBS#4?64%fS?5J9NzA>o@l`V+rYPF;Nkme>&@EGL5Hjiy_K*f2&9kbd)6v3> z)!)S3qTF`a8nD{XZUT66qJP$7CYQb${MfiOa1&){^aazqv5$kNg?VKAtqmmldBe?X-?ahAeZT7w&XN62mNilX&t*9{gjUeP>qnwX;q> z**3F!-*>l*P$xHI%vzq(!Nip_C(3p1h(k7jLde~|7=3qK_leGyi z{98JGLinAyMAky*C{2v?OMBt@=7{8y&BS5WaUA`95lK*J?HlR6)o-7eSES2crzcD0 z!>mYsw!-Y`^W~pnZ@y2*NvRz+^b9-aOua#EF?4?P^Hm)-m6YnB3@hdh`60(?|N2{& z`9rbT_iqFLMV{$BTbmCagd}0)hgsb_IMtRub%g@Nm@Pd3Id?sd&tATc!@W)zZ(t#l zR12kU<@!mV<&!k~jz!2fXZ_Z<`)u%Q;U`0`Tv$TF)_s5w!zbpT3U6#M{+?KLpzEZJ zerk71=@N=mkviDUB4lOzzGl1c6hE?bc|fzLhAOw*mOJUVWI1*AHlG-u?fdHOz8aBR zW+j2+976@lO6W$v(hMupNimFROumppx}$zSa-Xg3=Cv_t6d`ck=| zf#3+hfr_Tcxg_GmY=ThNu|UC$);9-#@xr%8+wm=guq%uR1lq5(bobaqGcXhO-JVYj zm-rPrE7bKGS1;t{!t(MZv`U*F|G}IBWpsJ6#xSN)R5B$1a_&!4d18cS?9Kcf+H?%|R@2@>2~Ia=j!ynYTcHd; zZWUYwSC@+U+?^DVXi&m&0xb~L!TOY?-)Iy=>%1)(EKE7#s4&Q#{f~W)vL8vEe?Cf3dqbXaK49TuA%FZ-DslD55 z7|2RlhHs{n6=b30ls%F}dT(nCQ+vO8{7K04=P%Pom1A4JPJt$fv`hCdb_6SweY{-! z&a7QWBS0pU3GsrpuO!`*11iw&<8CM9c9$gIWRc;SRymS+-dnhu^^G&hhL?jv@RKyR zNLY>;d{u@?mN||L-vYU7D8+vKmNif~Fnxs@9Rxqg%{rgmw17WJ<3K1uF_e3ig} z(SVX^{hG*#FZ{j`l<5fVFl$mNGw0J6XIltZLh7tdka$9siX{ zNHzZ_4}_8fZH3)et!ieW&IS45c3J~&FWnFxZ=Ik-YrG5WJHs0+87dj7{lFX|4u&v> z<3Y4AW<7K0xnIO3g)B1QIeHr!z-zt0%A7@($>gB8-kBk9`rewD0_Inuw_$(d$h7VY zsZmg2fryI|7L1~vZ0To>-frGXE(K`_ns7?412-ww= sR75AY@?DpsgV*T?8yX4Is*)e$TS~PkGnSXVxa|cP>6z;`Xk+952Y^KLq5uE@ literal 0 HcmV?d00001 diff --git a/src/main/webapp/content/images/jhipster_family_member_3.svg b/src/main/webapp/content/images/jhipster_family_member_3.svg new file mode 100644 index 00000000..cc0d01f1 --- /dev/null +++ b/src/main/webapp/content/images/jhipster_family_member_3.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/main/webapp/content/images/jhipster_family_member_3_head-192.png b/src/main/webapp/content/images/jhipster_family_member_3_head-192.png new file mode 100644 index 0000000000000000000000000000000000000000..b1e4fb3c8654570012950a3c888f11cb92c23bdd GIT binary patch literal 6148 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 B<5&Oy literal 0 HcmV?d00001 diff --git a/src/main/webapp/content/images/jhipster_family_member_3_head-256.png b/src/main/webapp/content/images/jhipster_family_member_3_head-256.png new file mode 100644 index 0000000000000000000000000000000000000000..aa058c7a0aba654a4991c8bbd5d3e2d32517acd1 GIT binary patch literal 8028 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 I)*|@-0J^?0w*UYD literal 0 HcmV?d00001 diff --git a/src/main/webapp/content/images/jhipster_family_member_3_head-384.png b/src/main/webapp/content/images/jhipster_family_member_3_head-384.png new file mode 100644 index 0000000000000000000000000000000000000000..1d10bd5da3d4d417aedfd3bf2e8515eff092c506 GIT binary patch literal 11998 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 element. + +$body-bg: #e4e5e6; + +// Typography: +// Font, line-height, and color for body text, headings, and more. + +$font-size-base: 1rem; + +$dropdown-link-hover-color: white; +$dropdown-link-hover-bg: #343a40; diff --git a/src/main/webapp/content/scss/global.scss b/src/main/webapp/content/scss/global.scss new file mode 100644 index 00000000..254cecb0 --- /dev/null +++ b/src/main/webapp/content/scss/global.scss @@ -0,0 +1,239 @@ +@import 'bootstrap-variables'; +@import '~bootstrap/scss/functions'; +@import '~bootstrap/scss/variables'; + +/* ============================================================== +Bootstrap tweaks +===============================================================*/ + +body, +h1, +h2, +h3, +h4 { + font-weight: 300; +} + +/* Increase contrast of links to get 100% on Lighthouse Accessability Audit. Override this color if you want to change the link color, or use a Bootswatch theme */ +a { + color: #533f03; + font-weight: bold; +} + +a:hover { + color: #533f03; +} + +/* override hover color for dropdown-item forced by bootstrap to all a:not([href]):not([tabindex]) elements in _reboot.scss */ +a:not([href]):not([tabindex]):hover.dropdown-item { + color: $dropdown-link-hover-color; +} + +/* override .dropdown-item.active background-color on hover */ +.dropdown-item.active:hover { + background-color: mix($dropdown-link-hover-bg, $dropdown-link-active-bg, 50%); +} + +a:hover { + /* make sure browsers use the pointer cursor for anchors, even with no href */ + cursor: pointer; +} + +.dropdown-item:hover { + color: $dropdown-link-hover-color; +} + +/* ========================================================================== +Browser Upgrade Prompt +========================================================================== */ +.browserupgrade { + margin: 0.2em 0; + background: #ccc; + color: #000; + padding: 0.2em 0; +} + +/* ========================================================================== +Generic styles +========================================================================== */ + +/* Error highlight on input fields */ +.ng-valid[required], +.ng-valid.required { + border-left: 5px solid green; +} + +.ng-invalid:not(form) { + border-left: 5px solid red; +} + +/* other generic styles */ + +.jh-card { + padding: 1.5%; + margin-top: 20px; + border: none; +} + +.error { + color: white; + background-color: red; +} + +.pad { + padding: 10px; +} + +.w-40 { + width: 40% !important; +} + +.w-60 { + width: 60% !important; +} + +.break { + white-space: normal; + word-break: break-all; +} + +.form-control { + background-color: #fff; +} + +.readonly { + background-color: #eee; + opacity: 1; +} + +.footer { + border-top: 1px solid rgba(0, 0, 0, 0.125); +} + +.hand, +[jhisortby] { + cursor: pointer; +} + +/* ========================================================================== +Custom alerts for notification +========================================================================== */ +.alerts { + .alert { + text-overflow: ellipsis; + pre { + background: none; + border: none; + font: inherit; + color: inherit; + padding: 0; + margin: 0; + } + .popover pre { + font-size: 10px; + } + } + .jhi-toast { + position: fixed; + width: 100%; + &.left { + left: 5px; + } + &.right { + right: 5px; + } + &.top { + top: 55px; + } + &.bottom { + bottom: 55px; + } + } +} + +@media screen and (min-width: 480px) { + .alerts .jhi-toast { + width: 50%; + } +} + +/* ========================================================================== +entity list page css +========================================================================== */ + +.table-entities thead th .d-flex > * { + margin: auto 0; +} + +/* ========================================================================== +entity detail page css +========================================================================== */ +.row-md.jh-entity-details { + display: grid; + grid-template-columns: auto 1fr; + column-gap: 10px; + line-height: 1.5; +} + +@media screen and (min-width: 768px) { + .row-md.jh-entity-details > { + dt { + float: left; + overflow: hidden; + clear: left; + text-align: right; + text-overflow: ellipsis; + white-space: nowrap; + padding: 0.5em 0; + } + dd { + border-bottom: 1px solid #eee; + padding: 0.5em 0; + margin-left: 0; + } + } +} + +/* ========================================================================== +ui bootstrap tweaks +========================================================================== */ +.nav, +.pagination, +.carousel, +.panel-title a { + cursor: pointer; +} + +.thread-dump-modal-lock { + max-width: 450px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.dropdown-menu { + padding-left: 0px; +} + +/* ========================================================================== +angular-cli removes postcss-rtl processed inline css, processed rules must be added here instead +========================================================================== */ +/* page-ribbon.component.scss */ +.ribbon { + left: -3.5em; + -moz-transform: rotate(-45deg); + -ms-transform: rotate(-45deg); + -o-transform: rotate(-45deg); + -webkit-transform: rotate(-45deg); + transform: rotate(-45deg); +} + +/* navbar.component.scss */ +.navbar { + ul.navbar-nav { + .nav-item { + margin-left: 0.5em; + } + } +} +/* jhipster-needle-scss-add-main JHipster will add new css style */ diff --git a/src/main/webapp/content/scss/vendor.scss b/src/main/webapp/content/scss/vendor.scss new file mode 100644 index 00000000..ce1c774a --- /dev/null +++ b/src/main/webapp/content/scss/vendor.scss @@ -0,0 +1,12 @@ +/* after changing this file run 'npm run webapp:build' */ + +/*************************** +put Sass variables here: +eg $input-color: red; +****************************/ +// Override Bootstrap variables +@import 'bootstrap-variables'; +// Import Bootstrap source files from node_modules +@import '~bootstrap/scss/bootstrap'; + +/* jhipster-needle-scss-add-vendor JHipster will add new css style */ diff --git a/src/main/webapp/declarations.d.ts b/src/main/webapp/declarations.d.ts new file mode 100644 index 00000000..38352e9b --- /dev/null +++ b/src/main/webapp/declarations.d.ts @@ -0,0 +1,2 @@ +declare const SERVER_API_URL: string; +declare const I18N_HASH: string; diff --git a/src/main/webapp/favicon.ico b/src/main/webapp/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..4179874f53b3c3b0ba9e2a401412f814ab6296bd GIT binary patch literal 1574 zcmV+>2HE+EP)000H;Nkl9r4a2J+cS@yzSX6E!^*k!$-D-4;C?DsI4Z2te8^PT@c z=RfD)VMK(b3{GnU7K#)Bt&t+2HBtn$Mv8#eNDUS>&~)z8>N1c(q8rNd{sPjn1MM2QHDA^sG2W;HKsEVpi{%G+8~m}54A zpq~8zXet!#9Gbj-*V8>RHQ@_Oa=ck5fDynmyijTRbXS$xsGFssxLtW3`my8G-!>%7 zn;n<%&U37xG-qc+$&NL}Ic8(r8P8{L>@pz`5wF~FxA(hl3{Q#@0mJ|T!>orWW&$y= zwZ)lV?q9=mOj&=001@Fo=j2<5|CsUovve~87<4?ht)^g4)54xHIM_fH6g9vTH~{$6e3n@@!>>0A((rb8tLK5s#V0 zMm+wn&))p*T>q|hCGR#@SL9}ZK#Tw|V#*5$4}y`?UCz_p4!1wNkO1l#@e&9G#+VYs zEG&vW!|wTsW3iPFM8p#vgQq%eFRm{7prxk1*aAS&G~ti@^w-GQ9%mpd0W^=8Nrc@q z?FmG(O?n~{0D#CgKIQg@lG#5`2LTaLZtu09`*&npbwOKeU4B;jv8(n!ddI?|{BT~F zzml*h^*el9D=mnp(SK|%RkC6{JP5dS>;C22j@;ZLQI=y-twP>a1a%F2w^mvhW1N!C zItRzq%;^AP-WFw5y#6R|j(8Qh9Dt}K_u4&+p(d6Y7r5u2fMIuVYB~yqz^KPR<_)T> zVUZCQ2K>q>IWj4>kGx1p%HD(9OEwu=KUlGU-EF%U2| z6%r-`VTl%YwkK@x(j4)7%q9E||Yz*Vu z-KgXDY^wP1l~c9KAAp;kIku<%ZR4V3H)gcci^L zxuYQ7iwPXJy}sz+_S%}ltQn(|jw6aU+5iCCMBw-}`=x=2s3a#J7eur?P5(n%lfW3; zzojxs0t_(d_}%ME4>VV=%*&kl@mY?4RLIPDrr1%QWBTmX>U-{zUphzI`^LkPoTQkY z^?7=MW3nvEM4$hB{Zyw7M3hi@DgLIIc}3Z#J)0`_Hm$Unjj=qofc>keP`dJ1mj{ks?4R(3kPwGF$MQ4Nw$&8u zY$#b@Zsp>+MfD;l;gbiMsB74J{+6r5=JEI=N`S;Tl0o2i)aJImRADmkV6kfzMM5Yl zb`4GR+C9Ed)T9?ySkhM)WtCdZJg310p5oRacks5uH}YWG9~KQdzS3&iP?nXGu8&`< zB@h73l>ryEsGJ*5{SM`E0!tK2{&F`(Kx?E3XpIyBt&t+2HBtn$Mv8#eNDYour user account has been activated. Please ", + "error": "Your user could not be activated. Please use the registration form to sign up." + } + } +} diff --git a/src/main/webapp/i18n/en/bloodPressure.json b/src/main/webapp/i18n/en/bloodPressure.json new file mode 100644 index 00000000..d3307ba8 --- /dev/null +++ b/src/main/webapp/i18n/en/bloodPressure.json @@ -0,0 +1,28 @@ +{ + "twentyOnePointsApp": { + "bloodPressure": { + "home": { + "title": "Blood Pressures", + "refreshListLabel": "Refresh list", + "createLabel": "Create a new Blood Pressure", + "createOrEditLabel": "Create or edit a Blood Pressure", + "search": "Search for Blood Pressure", + "notFound": "No Blood Pressures found" + }, + "created": "A new Blood Pressure is created with identifier {{ param }}", + "updated": "A Blood Pressure is updated with identifier {{ param }}", + "deleted": "A Blood Pressure is deleted with identifier {{ param }}", + "delete": { + "question": "Are you sure you want to delete Blood Pressure {{ id }}?" + }, + "detail": { + "title": "Blood Pressure" + }, + "id": "ID", + "timestamp": "Timestamp", + "systolic": "Systolic", + "diastolic": "Diastolic", + "user": "User" + } + } +} diff --git a/src/main/webapp/i18n/en/configuration.json b/src/main/webapp/i18n/en/configuration.json new file mode 100644 index 00000000..09c9a1af --- /dev/null +++ b/src/main/webapp/i18n/en/configuration.json @@ -0,0 +1,10 @@ +{ + "configuration": { + "title": "Configuration", + "filter": "Filter (by prefix)", + "table": { + "prefix": "Prefix", + "properties": "Properties" + } + } +} diff --git a/src/main/webapp/i18n/en/elasticsearch-reindex.json b/src/main/webapp/i18n/en/elasticsearch-reindex.json new file mode 100644 index 00000000..4b5d3636 --- /dev/null +++ b/src/main/webapp/i18n/en/elasticsearch-reindex.json @@ -0,0 +1,18 @@ +{ + "elasticsearch": { + "reindex": { + "accepted": "Request accepted, performing full Elasticsearch reindexing.", + "acceptedSelected": "Request accepted, performing partial Elasticsearch reindexing.", + "dialog": { + "title": "Confirm Elasticsearch Reindex", + "question": "Are you sure you want to reindex Elasticsearch? This could take a while!", + "cancel": "Cancel", + "confirm": "Reindex" + }, + "title": "Reindex Elasticsearch", + "instructions1": "This will send a request to reindex all data from your primary datasource into Elasticsearch. This will fix missing data and mapping issues, however you will lose any ES data and mapping that was defined outside of your application. This request will return immediately and the reindexing will run in the background. You will see \"Elasticsearch: Successfully performed reindexing\" in your logs when this is complete.", + "instructions2": "This can take a long time and is susceptible to concurrency issues so it should be performed when the number of users is low.", + "go": "Go" + } + } +} diff --git a/src/main/webapp/i18n/en/error.json b/src/main/webapp/i18n/en/error.json new file mode 100644 index 00000000..d2575632 --- /dev/null +++ b/src/main/webapp/i18n/en/error.json @@ -0,0 +1,14 @@ +{ + "error": { + "title": "Error page!", + "http": { + "400": "Bad request.", + "403": "You are not authorized to access this page.", + "404": "The page does not exist.", + "405": "The HTTP verb you used is not supported for this URL.", + "500": "Internal server error." + }, + "concurrencyFailure": "Another user modified this data at the same time as you. Your changes were rejected.", + "validation": "Validation error on the server." + } +} diff --git a/src/main/webapp/i18n/en/global.json b/src/main/webapp/i18n/en/global.json new file mode 100644 index 00000000..40d44730 --- /dev/null +++ b/src/main/webapp/i18n/en/global.json @@ -0,0 +1,152 @@ +{ + "global": { + "title": "TwentyOnePoints", + "browsehappy": "You are using an outdated browser. Please upgrade your browser to improve your experience.", + "menu": { + "home": "Home", + "jhipster-needle-menu-add-element": "JHipster will add additional menu entries here (do not translate!)", + "entities": { + "main": "Entities", + "points": "Points", + "weight": "Weight", + "bloodPressure": "Blood Pressure", + "preferences": "Preferences", + "jhipster-needle-menu-add-entry": "JHipster will add additional entities here (do not translate!)" + }, + "account": { + "main": "Account", + "settings": "Settings", + "password": "Password", + "sessions": "Sessions", + "login": "Sign in", + "logout": "Sign out", + "register": "Register" + }, + "admin": { + "main": "Administration", + "userManagement": "User management", + "tracker": "User tracker", + "metrics": "Metrics", + "health": "Health", + "configuration": "Configuration", + "logs": "Logs", + "apidocs": "API", + "database": "Database", + "elasticsearch-reindex": "Reindex Elasticsearch", + "jhipster-needle-menu-add-admin-element": "JHipster will add additional menu entries here (do not translate!)" + }, + "language": "Language" + }, + "form": { + "username.label": "Username", + "username.placeholder": "Your username", + "currentpassword.label": "Current password", + "currentpassword.placeholder": "Current password", + "newpassword.label": "New password", + "newpassword.placeholder": "New password", + "confirmpassword.label": "New password confirmation", + "confirmpassword.placeholder": "Confirm the new password", + "email.label": "Email", + "email.placeholder": "Your email" + }, + "messages": { + "info": { + "authenticated": { + "prefix": "If you want to ", + "link": "sign in", + "suffix": ", you can try the default accounts:
- Administrator (login=\"admin\" and password=\"admin\")
- User (login=\"user\" and password=\"user\")." + }, + "register": { + "noaccount": "You don't have an account yet?", + "link": "Register a new account" + } + }, + "error": { + "dontmatch": "The password and its confirmation do not match!" + }, + "validate": { + "newpassword": { + "required": "Your password is required.", + "minlength": "Your password is required to be at least 4 characters.", + "maxlength": "Your password cannot be longer than 50 characters.", + "strength": "Password strength:" + }, + "confirmpassword": { + "required": "Your confirmation password is required.", + "minlength": "Your confirmation password is required to be at least 4 characters.", + "maxlength": "Your confirmation password cannot be longer than 50 characters." + }, + "email": { + "required": "Your email is required.", + "invalid": "Your email is invalid.", + "minlength": "Your email is required to be at least 5 characters.", + "maxlength": "Your email cannot be longer than 50 characters." + } + } + }, + "field": { + "id": "ID" + }, + "ribbon": { + "dev": "Development" + }, + "item-count": "Showing {{first}} - {{second}} of {{total}} items." + }, + "entity": { + "action": { + "addblob": "Add blob", + "addimage": "Add image", + "back": "Back", + "cancel": "Cancel", + "delete": "Delete", + "edit": "Edit", + "open": "Open", + "save": "Save", + "view": "View", + "show": "Show {{otherEntity}}" + }, + "detail": { + "field": "Field", + "value": "Value" + }, + "delete": { + "title": "Confirm delete operation" + }, + "validation": { + "required": "This field is required.", + "minlength": "This field is required to be at least {{ min }} characters.", + "maxlength": "This field cannot be longer than {{ max }} characters.", + "min": "This field should be at least {{ min }}.", + "max": "This field cannot be more than {{ max }}.", + "minbytes": "This field should be at least {{ min }} bytes.", + "maxbytes": "This field cannot be more than {{ max }} bytes.", + "pattern": "This field should follow pattern for {{ pattern }}.", + "number": "This field should be a number.", + "datetimelocal": "This field should be a date and time.", + "patternLogin": "This field can only contain letters, digits and e-mail addresses." + }, + "filters": { + "set": "Following filters are set", + "clear": "Clear filter", + "clearAll": "Clear all filters" + } + }, + "error": { + "internalServerError": "Internal server error", + "server.not.reachable": "Server not reachable", + "url.not.found": "Not found", + "NotNull": "Field {{ fieldName }} cannot be empty!", + "Size": "Field {{ fieldName }} does not meet min/max size requirements!", + "userexists": "Login name already used!", + "emailexists": "Email is already in use!", + "idexists": "A new {{ entityName }} cannot already have an ID", + "idnull": "Invalid ID", + "idinvalid": "Invalid ID", + "idnotfound": "ID cannot be found", + "file": { + "could.not.extract": "Could not extract file", + "not.image": "File was expected to be an image but was found to be \"{{ fileType }}\"" + } + }, + "footer": "This is your footer" +} diff --git a/src/main/webapp/i18n/en/health.json b/src/main/webapp/i18n/en/health.json new file mode 100644 index 00000000..b7f4daa4 --- /dev/null +++ b/src/main/webapp/i18n/en/health.json @@ -0,0 +1,33 @@ +{ + "health": { + "title": "Health Checks", + "refresh.button": "Refresh", + "stacktrace": "Stacktrace", + "details": { + "details": "Details", + "properties": "Properties", + "name": "Name", + "value": "Value", + "error": "Error" + }, + "indicator": { + "diskSpace": "Disk space", + "mail": "Email", + "livenessState": "Liveness state", + "readinessState": "Readiness state", + "ping": "Application", + "elasticsearch": "Elasticsearch", + "db": "Database" + }, + "table": { + "service": "Service name", + "status": "Status" + }, + "status": { + "UNKNOWN": "UNKNOWN", + "UP": "UP", + "OUT_OF_SERVICE": "OUT_OF_SERVICE", + "DOWN": "DOWN" + } + } +} diff --git a/src/main/webapp/i18n/en/home.json b/src/main/webapp/i18n/en/home.json new file mode 100644 index 00000000..d9fc4990 --- /dev/null +++ b/src/main/webapp/i18n/en/home.json @@ -0,0 +1,19 @@ +{ + "home": { + "title": "Welcome, Java Hipster!", + "subtitle": "This is your homepage", + "logged": { + "message": "You are logged in as user \"{{username}}\"." + }, + "question": "If you have any question on JHipster:", + "link": { + "homepage": "JHipster homepage", + "stackoverflow": "JHipster on Stack Overflow", + "bugtracker": "JHipster bug tracker", + "chat": "JHipster public chat room", + "follow": "follow @jhipster on Twitter" + }, + "like": "If you like JHipster, don't forget to give us a star on", + "github": "GitHub" + } +} diff --git a/src/main/webapp/i18n/en/login.json b/src/main/webapp/i18n/en/login.json new file mode 100644 index 00000000..46679588 --- /dev/null +++ b/src/main/webapp/i18n/en/login.json @@ -0,0 +1,19 @@ +{ + "login": { + "title": "Sign in", + "form": { + "password": "Password", + "password.placeholder": "Your password", + "rememberme": "Remember me", + "button": "Sign in" + }, + "messages": { + "error": { + "authentication": "Failed to sign in! Please check your credentials and try again." + } + }, + "password": { + "forgot": "Did you forget your password?" + } + } +} diff --git a/src/main/webapp/i18n/en/logs.json b/src/main/webapp/i18n/en/logs.json new file mode 100644 index 00000000..8ee0ba5a --- /dev/null +++ b/src/main/webapp/i18n/en/logs.json @@ -0,0 +1,11 @@ +{ + "logs": { + "title": "Logs", + "nbloggers": "There are {{ total }} loggers.", + "filter": "Filter", + "table": { + "name": "Name", + "level": "Level" + } + } +} diff --git a/src/main/webapp/i18n/en/metrics.json b/src/main/webapp/i18n/en/metrics.json new file mode 100644 index 00000000..c514dee7 --- /dev/null +++ b/src/main/webapp/i18n/en/metrics.json @@ -0,0 +1,102 @@ +{ + "metrics": { + "title": "Application Metrics", + "refresh.button": "Refresh", + "updating": "Updating...", + "jvm": { + "title": "JVM Metrics", + "memory": { + "title": "Memory", + "total": "Total Memory", + "heap": "Heap Memory", + "nonheap": "Non-Heap Memory" + }, + "threads": { + "title": "Threads", + "all": "All", + "runnable": "Runnable", + "timedwaiting": "Timed waiting", + "waiting": "Waiting", + "blocked": "Blocked", + "dump": { + "title": "Threads dump", + "id": "Id: ", + "blockedtime": "Blocked Time", + "blockedcount": "Blocked Count", + "waitedtime": "Waited Time", + "waitedcount": "Waited Count", + "lockname": "Lock name", + "stacktrace": "Stacktrace", + "show": "Show Stacktrace", + "hide": "Hide Stacktrace" + } + }, + "gc": { + "title": "Garbage collections", + "marksweepcount": "Mark Sweep count", + "marksweeptime": "Mark Sweep time", + "scavengecount": "Scavenge count", + "scavengetime": "Scavenge time" + }, + "http": { + "title": "HTTP requests (time in millisecond)", + "active": "Active requests:", + "total": "Total requests:", + "table": { + "code": "Code", + "count": "Count", + "mean": "Mean", + "average": "Average", + "max": "Max" + }, + "code": { + "ok": "Ok", + "notfound": "Not found", + "servererror": "Server Error" + } + } + }, + "servicesstats": { + "title": "Services statistics (time in millisecond)", + "table": { + "name": "Service name", + "count": "Count", + "mean": "Mean", + "min": "Min", + "max": "Max", + "p50": "p50", + "p75": "p75", + "p95": "p95", + "p99": "p99" + } + }, + "cache": { + "title": "Cache statistics", + "cachename": "Cache name", + "hits": "Cache Hits", + "misses": "Cache Misses", + "gets": "Cache Gets", + "puts": "Cache Puts", + "removals": "Cache Removals", + "evictions": "Cache Evictions", + "hitPercent": "Cache Hit %", + "missPercent": "Cache Miss %", + "averageGetTime": "Average get time (µs)", + "averagePutTime": "Average put time (µs)", + "averageRemoveTime": "Average remove time (µs)" + }, + "datasource": { + "usage": "Connection Pool Usage", + "title": "DataSource statistics (time in millisecond)", + "name": "Pool usage", + "count": "Count", + "mean": "Mean", + "min": "Min", + "max": "Max", + "p50": "p50", + "p75": "p75", + "p95": "p95", + "p99": "p99" + } + } +} diff --git a/src/main/webapp/i18n/en/password.json b/src/main/webapp/i18n/en/password.json new file mode 100644 index 00000000..fc9b6c94 --- /dev/null +++ b/src/main/webapp/i18n/en/password.json @@ -0,0 +1,12 @@ +{ + "password": { + "title": "Password for [{{username}}]", + "form": { + "button": "Save" + }, + "messages": { + "error": "An error has occurred! The password could not be changed.", + "success": "Password changed!" + } + } +} diff --git a/src/main/webapp/i18n/en/points.json b/src/main/webapp/i18n/en/points.json new file mode 100644 index 00000000..2863175e --- /dev/null +++ b/src/main/webapp/i18n/en/points.json @@ -0,0 +1,30 @@ +{ + "twentyOnePointsApp": { + "points": { + "home": { + "title": "Points", + "refreshListLabel": "Refresh list", + "createLabel": "Create a new Points", + "createOrEditLabel": "Create or edit a Points", + "search": "Search for Points", + "notFound": "No Points found" + }, + "created": "A new Points is created with identifier {{ param }}", + "updated": "A Points is updated with identifier {{ param }}", + "deleted": "A Points is deleted with identifier {{ param }}", + "delete": { + "question": "Are you sure you want to delete Points {{ id }}?" + }, + "detail": { + "title": "Points" + }, + "id": "ID", + "date": "Date", + "exercise": "Exercise", + "meals": "Meals", + "alcohol": "Alcohol", + "notes": "Notes", + "user": "User" + } + } +} diff --git a/src/main/webapp/i18n/en/preferences.json b/src/main/webapp/i18n/en/preferences.json new file mode 100644 index 00000000..63d04d6b --- /dev/null +++ b/src/main/webapp/i18n/en/preferences.json @@ -0,0 +1,27 @@ +{ + "twentyOnePointsApp": { + "preferences": { + "home": { + "title": "Preferences", + "refreshListLabel": "Refresh list", + "createLabel": "Create a new Preferences", + "createOrEditLabel": "Create or edit a Preferences", + "search": "Search for Preferences", + "notFound": "No Preferences found" + }, + "created": "A new Preferences is created with identifier {{ param }}", + "updated": "A Preferences is updated with identifier {{ param }}", + "deleted": "A Preferences is deleted with identifier {{ param }}", + "delete": { + "question": "Are you sure you want to delete Preferences {{ id }}?" + }, + "detail": { + "title": "Preferences" + }, + "id": "ID", + "weeklyGoal": "Weekly Goal", + "weightUnits": "Weight Units", + "user": "User" + } + } +} diff --git a/src/main/webapp/i18n/en/register.json b/src/main/webapp/i18n/en/register.json new file mode 100644 index 00000000..1a0aeecf --- /dev/null +++ b/src/main/webapp/i18n/en/register.json @@ -0,0 +1,24 @@ +{ + "register": { + "title": "Registration", + "form": { + "button": "Register" + }, + "messages": { + "validate": { + "login": { + "required": "Your username is required.", + "minlength": "Your username is required to be at least 1 character.", + "maxlength": "Your username cannot be longer than 50 characters.", + "pattern": "Your username is invalid." + } + }, + "success": "Registration saved! Please check your email for confirmation.", + "error": { + "fail": "Registration failed! Please try again later.", + "userexists": "Login name already registered! Please choose another one.", + "emailexists": "Email is already in use! Please choose another one." + } + } + } +} diff --git a/src/main/webapp/i18n/en/reset.json b/src/main/webapp/i18n/en/reset.json new file mode 100644 index 00000000..4c35e9a2 --- /dev/null +++ b/src/main/webapp/i18n/en/reset.json @@ -0,0 +1,26 @@ +{ + "reset": { + "request": { + "title": "Reset your password", + "form": { + "button": "Reset password" + }, + "messages": { + "info": "Enter the email address you used to register", + "success": "Check your emails for details on how to reset your password." + } + }, + "finish": { + "title": "Reset password", + "form": { + "button": "Validate new password" + }, + "messages": { + "info": "Choose a new password", + "success": "Your password has been reset. Please ", + "keymissing": "The reset key is missing.", + "error": "Your password couldn't be reset. Remember a password request is only valid for 24 hours." + } + } + } +} diff --git a/src/main/webapp/i18n/en/sessions.json b/src/main/webapp/i18n/en/sessions.json new file mode 100644 index 00000000..38bf44f7 --- /dev/null +++ b/src/main/webapp/i18n/en/sessions.json @@ -0,0 +1,15 @@ +{ + "sessions": { + "title": "Active sessions for [{{username}}]", + "table": { + "ipaddress": "IP address", + "useragent": "User Agent", + "date": "Date", + "button": "Invalidate" + }, + "messages": { + "success": "Session invalidated!", + "error": "An error has occurred! The session could not be invalidated." + } + } +} diff --git a/src/main/webapp/i18n/en/settings.json b/src/main/webapp/i18n/en/settings.json new file mode 100644 index 00000000..508c037c --- /dev/null +++ b/src/main/webapp/i18n/en/settings.json @@ -0,0 +1,32 @@ +{ + "settings": { + "title": "User settings for [{{username}}]", + "form": { + "firstname": "First Name", + "firstname.placeholder": "Your first name", + "lastname": "Last Name", + "lastname.placeholder": "Your last name", + "language": "Language", + "button": "Save" + }, + "messages": { + "error": { + "fail": "An error has occurred! Settings could not be saved.", + "emailexists": "Email is already in use! Please choose another one." + }, + "success": "Settings saved!", + "validate": { + "firstname": { + "required": "Your first name is required.", + "minlength": "Your first name is required to be at least 1 character", + "maxlength": "Your first name cannot be longer than 50 characters" + }, + "lastname": { + "required": "Your last name is required.", + "minlength": "Your last name is required to be at least 1 character", + "maxlength": "Your last name cannot be longer than 50 characters" + } + } + } + } +} diff --git a/src/main/webapp/i18n/en/units.json b/src/main/webapp/i18n/en/units.json new file mode 100644 index 00000000..3298e8f6 --- /dev/null +++ b/src/main/webapp/i18n/en/units.json @@ -0,0 +1,9 @@ +{ + "twentyOnePointsApp": { + "Units": { + "null": "", + "KG": "KG", + "LB": "LB" + } + } +} diff --git a/src/main/webapp/i18n/en/user-management.json b/src/main/webapp/i18n/en/user-management.json new file mode 100644 index 00000000..52b56a1a --- /dev/null +++ b/src/main/webapp/i18n/en/user-management.json @@ -0,0 +1,31 @@ +{ + "userManagement": { + "home": { + "title": "Users", + "refreshListLabel": "Refresh list", + "createLabel": "Create a new user", + "createOrEditLabel": "Create or edit a user" + }, + "created": "A new user is created with identifier {{ param }}", + "updated": "A user is updated with identifier {{ param }}", + "deleted": "A user is deleted with identifier {{ param }}", + "delete": { + "question": "Are you sure you want to delete user {{ login }}?" + }, + "detail": { + "title": "User" + }, + "login": "Login", + "firstName": "First name", + "lastName": "Last name", + "email": "Email", + "activated": "Activated", + "deactivated": "Deactivated", + "profiles": "Profiles", + "langKey": "Language", + "createdBy": "Created by", + "createdDate": "Created date", + "lastModifiedBy": "Modified by", + "lastModifiedDate": "Modified date" + } +} diff --git a/src/main/webapp/i18n/en/weight.json b/src/main/webapp/i18n/en/weight.json new file mode 100644 index 00000000..ff73fdbb --- /dev/null +++ b/src/main/webapp/i18n/en/weight.json @@ -0,0 +1,27 @@ +{ + "twentyOnePointsApp": { + "weight": { + "home": { + "title": "Weights", + "refreshListLabel": "Refresh list", + "createLabel": "Create a new Weight", + "createOrEditLabel": "Create or edit a Weight", + "search": "Search for Weight", + "notFound": "No Weights found" + }, + "created": "A new Weight is created with identifier {{ param }}", + "updated": "A Weight is updated with identifier {{ param }}", + "deleted": "A Weight is deleted with identifier {{ param }}", + "delete": { + "question": "Are you sure you want to delete Weight {{ id }}?" + }, + "detail": { + "title": "Weight" + }, + "id": "ID", + "timestamp": "Timestamp", + "weight": "Weight", + "user": "User" + } + } +} diff --git a/src/main/webapp/i18n/fr/activate.json b/src/main/webapp/i18n/fr/activate.json new file mode 100644 index 00000000..7b29d283 --- /dev/null +++ b/src/main/webapp/i18n/fr/activate.json @@ -0,0 +1,9 @@ +{ + "activate": { + "title": "Activation", + "messages": { + "success": "Votre compte utilisateur a été activé. Merci de vous ", + "error": "Votre compte utilisateur n'a pas pu être activé. Utilisez le formulaire d'enregistrement pour en créer un nouveau." + } + } +} diff --git a/src/main/webapp/i18n/fr/bloodPressure.json b/src/main/webapp/i18n/fr/bloodPressure.json new file mode 100644 index 00000000..9db50618 --- /dev/null +++ b/src/main/webapp/i18n/fr/bloodPressure.json @@ -0,0 +1,28 @@ +{ + "twentyOnePointsApp": { + "bloodPressure": { + "home": { + "title": "BloodPressures", + "refreshListLabel": "Actualiser la liste", + "createLabel": "Créer un nouveau Blood Pressure", + "createOrEditLabel": "Créer ou éditer un Blood Pressure", + "search": "Recherche pour Blood Pressure", + "notFound": "Aucun Blood Pressure trouvé" + }, + "created": "Un nouveau Blood Pressure a été créé avec l'identifiant {{ param }}", + "updated": "Le Blood Pressure avec l'identifiant {{ param }} a été mis à jour", + "deleted": "Le Blood Pressure avec l'identifiant {{ param }} a été supprimé", + "delete": { + "question": "Êtes-vous certain de vouloir supprimer le Blood Pressure {{ id }} ?" + }, + "detail": { + "title": "Blood Pressure" + }, + "id": "ID", + "timestamp": "Timestamp", + "systolic": "Systolic", + "diastolic": "Diastolic", + "user": "User" + } + } +} diff --git a/src/main/webapp/i18n/fr/configuration.json b/src/main/webapp/i18n/fr/configuration.json new file mode 100644 index 00000000..a9a014aa --- /dev/null +++ b/src/main/webapp/i18n/fr/configuration.json @@ -0,0 +1,10 @@ +{ + "configuration": { + "title": "Configuration", + "filter": "Filtrer (par préfixe)", + "table": { + "prefix": "Préfixe", + "properties": "Propriétés" + } + } +} diff --git a/src/main/webapp/i18n/fr/elasticsearch-reindex.json b/src/main/webapp/i18n/fr/elasticsearch-reindex.json new file mode 100644 index 00000000..4b5d3636 --- /dev/null +++ b/src/main/webapp/i18n/fr/elasticsearch-reindex.json @@ -0,0 +1,18 @@ +{ + "elasticsearch": { + "reindex": { + "accepted": "Request accepted, performing full Elasticsearch reindexing.", + "acceptedSelected": "Request accepted, performing partial Elasticsearch reindexing.", + "dialog": { + "title": "Confirm Elasticsearch Reindex", + "question": "Are you sure you want to reindex Elasticsearch? This could take a while!", + "cancel": "Cancel", + "confirm": "Reindex" + }, + "title": "Reindex Elasticsearch", + "instructions1": "This will send a request to reindex all data from your primary datasource into Elasticsearch. This will fix missing data and mapping issues, however you will lose any ES data and mapping that was defined outside of your application. This request will return immediately and the reindexing will run in the background. You will see \"Elasticsearch: Successfully performed reindexing\" in your logs when this is complete.", + "instructions2": "This can take a long time and is susceptible to concurrency issues so it should be performed when the number of users is low.", + "go": "Go" + } + } +} diff --git a/src/main/webapp/i18n/fr/error.json b/src/main/webapp/i18n/fr/error.json new file mode 100644 index 00000000..71622e54 --- /dev/null +++ b/src/main/webapp/i18n/fr/error.json @@ -0,0 +1,14 @@ +{ + "error": { + "title": "Page d'erreur !", + "http": { + "400": "Mauvaise requête.", + "403": "Vous n'avez pas les droits pour accéder à cette page.", + "404": "La page n'existe pas.", + "405": "Le verbe HTTP que vous avez utilisé n'est pas reconnu par cet URL.", + "500": "Erreur interne du serveur." + }, + "concurrencyFailure": "Un autre utilisateur a modifié ces données en même temps que vous. Vos changements n'ont pas été sauvegardés.", + "validation": "Erreur de validation côté serveur." + } +} diff --git a/src/main/webapp/i18n/fr/global.json b/src/main/webapp/i18n/fr/global.json new file mode 100644 index 00000000..847e48b9 --- /dev/null +++ b/src/main/webapp/i18n/fr/global.json @@ -0,0 +1,152 @@ +{ + "global": { + "title": "TwentyOnePoints", + "browsehappy": "Vous utilisez un ancien navigateur. Veuillez mettre à jour votre navigateur pour une meilleure expérience.", + "menu": { + "home": "Accueil", + "jhipster-needle-menu-add-element": "JHipster will add additional menu entries here (do not translate!)", + "entities": { + "main": "Entités", + "points": "Points", + "weight": "Weight", + "bloodPressure": "Blood Pressure", + "preferences": "Preferences", + "jhipster-needle-menu-add-entry": "JHipster will add additional entities here (do not translate!)" + }, + "account": { + "main": "Compte", + "settings": "Profil", + "password": "Mot de passe", + "sessions": "Sessions", + "login": "S'authentifier", + "logout": "Déconnexion", + "register": "Créer un compte" + }, + "admin": { + "main": "Administration", + "userManagement": "Gestion des utilisateurs", + "tracker": "Suivi des utilisateurs", + "metrics": "Métriques", + "health": "Diagnostics", + "configuration": "Configuration", + "logs": "Logs", + "apidocs": "API", + "database": "Base de données", + "elasticsearch-reindex": "Reindex Elasticsearch", + "jhipster-needle-menu-add-admin-element": "JHipster will add additional menu entries here (do not translate!)" + }, + "language": "Langue" + }, + "form": { + "username.label": "Nom d'utilisateur", + "username.placeholder": "Votre nom d'utilisateur", + "currentpassword.label": "Mot de passe actuel", + "currentpassword.placeholder": "Mot de passe actuel", + "newpassword.label": "Nouveau mot de passe", + "newpassword.placeholder": "Nouveau mot de passe", + "confirmpassword.label": "Confirmation du nouveau mot de passe", + "confirmpassword.placeholder": "Confirmation du nouveau mot de passe", + "email.label": "Email", + "email.placeholder": "Votre email" + }, + "messages": { + "info": { + "authenticated": { + "prefix": "Si vous voulez vous ", + "link": "connecter", + "suffix": ", vous pouvez utiliser les comptes par défaut :
- Administrateur (nom d'utilisateur=\"admin\" et mot de passe =\"admin\")
- Utilisateur (nom d'utilisateur=\"user\" et mot de passe =\"user\")." + }, + "register": { + "noaccount": "Vous n'avez pas encore de compte ?", + "link": "Créer un compte" + } + }, + "error": { + "dontmatch": "Le nouveau mot de passe et sa confirmation ne sont pas égaux !" + }, + "validate": { + "newpassword": { + "required": "Votre mot de passe est requis.", + "minlength": "Votre mot de passe doit comporter au moins 4 caractères.", + "maxlength": "Votre mot de passe ne doit pas comporter plus de 50 caractères.", + "strength": "Robustesse du mot de passe :" + }, + "confirmpassword": { + "required": "Votre confirmation du mot de passe est requise.", + "minlength": "Votre confirmation du mot de passe doit comporter au moins 4 caractères.", + "maxlength": "Votre confirmation du mot de passe ne doit pas comporter plus de 50 caractères." + }, + "email": { + "required": "Votre email est requis.", + "minlength": "Votre email doit comporter au moins 5 caractères.", + "maxlength": "Votre email ne doit pas comporter plus de 50 caractères.", + "invalid": "Votre email n'est pas valide." + } + } + }, + "field": { + "id": "ID" + }, + "ribbon": { + "dev": "Développement" + }, + "item-count": "Affichage {{first}} - {{second}} de {{total}} items." + }, + "entity": { + "action": { + "addblob": "Ajouter blob", + "addimage": "Ajouter image", + "back": "Retour", + "cancel": "Annuler", + "edit": "Editer", + "delete": "Supprimer", + "open": "Ouvrir", + "save": "Sauvegarder", + "view": "Voir", + "show": "Show {{otherEntity}}" + }, + "detail": { + "field": "Champs", + "value": "Valeur" + }, + "delete": { + "title": "Confirmation de suppression" + }, + "validation": { + "required": "Ce champ est obligatoire.", + "minlength": "Ce champ doit faire au minimum {{min}} caractères.", + "maxlength": "Ce champ doit faire moins de {{max}} caractères.", + "min": "Ce champ doit être supérieur à {{min}}.", + "max": "Ce champ doit être inférieur à {{max}}.", + "minbytes": "Ce champ doit être supérieur à {{min}} bytes.", + "maxbytes": "Ce champ doit être inférieur à {{max}} bytes.", + "pattern": "Ce champ doit suivre l'expression régulière {{pattern}}.", + "number": "Ce champ doit être un nombre.", + "datetimelocal": "Ce champ doit être une date et une heure.", + "patternLogin": "Ce champ ne peut contenir que des lettres, des chiffres ou des adresses e-mail." + }, + "filters": { + "set": "Following filters are set", + "clear": "Clear filter", + "clearAll": "Clear all filters" + } + }, + "error": { + "internalServerError": "Erreur interne du serveur", + "server.not.reachable": "Serveur inaccessible", + "url.not.found": "Non trouvé", + "NotNull": "Le champ {{fieldName}} ne peut pas être vide !", + "Size": "Le champ {{fieldName}} ne respecte pas les critères minimum et maximum !", + "userexists": "Login déjà utilisé !", + "emailexists": "Email déjà utilisé !", + "idexists": "Une nouvelle entité {{entityName}} ne peut pas avoir d'identifiant !", + "idnull": "Identifiant invalide", + "idinvalid": "Invalid Id", + "idnotfound": "ID cannot be found", + "file": { + "could.not.extract": "Impossible d'extraire le fichier", + "not.image": "Le fichier doit être une image et non du type \"{{ fileType }}\"" + } + }, + "footer": "Ceci est votre pied de page" +} diff --git a/src/main/webapp/i18n/fr/health.json b/src/main/webapp/i18n/fr/health.json new file mode 100644 index 00000000..e241ce78 --- /dev/null +++ b/src/main/webapp/i18n/fr/health.json @@ -0,0 +1,32 @@ +{ + "health": { + "title": "Diagnostics", + "refresh.button": "Rafraîchir", + "stacktrace": "Stacktrace", + "details": { + "details": "Détails", + "properties": "Propriétés", + "name": "Nom", + "value": "Valeur", + "error": "Erreur" + }, + "indicator": { + "diskSpace": "Espace disque", + "mail": "Email", + "livenessState": "Liveness state", + "readinessState": "Readiness state", + "ping": "Application", + "elasticsearch": "Elasticsearch", + "db": "Base de données" + }, + "table": { + "service": "Nom du service", + "status": "État" + }, + "status": { + "UNKNOWN": "INCONNU", + "UP": "DISPONIBLE", + "DOWN": "HORS SERVICE" + } + } +} diff --git a/src/main/webapp/i18n/fr/home.json b/src/main/webapp/i18n/fr/home.json new file mode 100644 index 00000000..44dc4b9a --- /dev/null +++ b/src/main/webapp/i18n/fr/home.json @@ -0,0 +1,19 @@ +{ + "home": { + "title": "Bienvenue, Java Hipster !", + "subtitle": "Ceci est votre page d'accueil", + "logged": { + "message": "Vous êtes connecté en tant que \"{{username}}\"." + }, + "question": "Si vous avez des questions à propos de JHipster :", + "link": { + "homepage": "Page d'accueil de JHipster", + "stackoverflow": "JHipster sur Stack Overflow", + "bugtracker": "JHipster bug tracker", + "chat": "JHipster public chat room", + "follow": "Suivez @jhipster sur Twitter" + }, + "like": "Si vous aimez JHipster, donnez nous une étoile sur", + "github": "GitHub " + } +} diff --git a/src/main/webapp/i18n/fr/login.json b/src/main/webapp/i18n/fr/login.json new file mode 100644 index 00000000..f5aa2af9 --- /dev/null +++ b/src/main/webapp/i18n/fr/login.json @@ -0,0 +1,19 @@ +{ + "login": { + "title": "Authentification", + "form": { + "password": "Mot de passe", + "password.placeholder": "Votre mot de passe", + "rememberme": "Garder la session ouverte", + "button": "Connexion" + }, + "messages": { + "error": { + "authentication": "Erreur d'authentification ! Veuillez vérifier vos identifiants de connexion." + } + }, + "password": { + "forgot": "Avez-vous oublié votre mot de passe ?" + } + } +} diff --git a/src/main/webapp/i18n/fr/logs.json b/src/main/webapp/i18n/fr/logs.json new file mode 100644 index 00000000..e0acb272 --- /dev/null +++ b/src/main/webapp/i18n/fr/logs.json @@ -0,0 +1,11 @@ +{ + "logs": { + "title": "Logs", + "nbloggers": "Total de {{ total }} \"loggers\".", + "filter": "Filtrer", + "table": { + "name": "Nom", + "level": "Niveau" + } + } +} diff --git a/src/main/webapp/i18n/fr/metrics.json b/src/main/webapp/i18n/fr/metrics.json new file mode 100644 index 00000000..cbd5c4a1 --- /dev/null +++ b/src/main/webapp/i18n/fr/metrics.json @@ -0,0 +1,102 @@ +{ + "metrics": { + "title": "Métriques de l'application", + "refresh.button": "Rafraîchir", + "updating": "Mise à jour...", + "jvm": { + "title": "Métriques de la JVM", + "memory": { + "title": "Mémoire", + "total": "Mémoire totale", + "heap": "Mémoire \"Heap\"", + "nonheap": "Mémoire \"Non-Heap\"" + }, + "threads": { + "title": "Threads", + "all": "Tous", + "runnable": "Exécutable", + "timedwaiting": "Temps d'attente", + "waiting": "En attente", + "blocked": "Bloqué", + "dump": { + "title": "Threads dump", + "id": "Id :", + "blockedtime": "Temps bloqué", + "blockedcount": "Nb fois bloqué", + "waitedtime": "Temps d'attente", + "waitedcount": "Nb fois en attente", + "lockname": "Nom du process bloquant", + "stacktrace": "Stacktrace", + "show": "Afficher", + "hide": "Cacher" + } + }, + "gc": { + "title": "Garbage collections", + "marksweepcount": "Total de \"Mark Sweep\"", + "marksweeptime": "Temps \"Mark Sweep\"", + "scavengecount": "Total \"Scavenge\"", + "scavengetime": "Temps \"Scavenge\"" + }, + "http": { + "title": "Requêtes HTTP (temps en millisecondes)", + "active": "Requêtes actives :", + "total": "Requêtes totales :", + "table": { + "code": "Code", + "count": "Total", + "mean": "Médian", + "average": "Moyenne", + "max": "Max" + }, + "code": { + "ok": "Ok", + "notfound": "Non trouvé", + "servererror": "Erreur serveur" + } + } + }, + "servicesstats": { + "title": "Statistiques des services (temps en millisecondes)", + "table": { + "name": "Nom du server", + "count": "Total", + "mean": "Médian", + "min": "Min", + "max": "Max", + "p50": "p50", + "p75": "p75", + "p95": "p95", + "p99": "p99" + } + }, + "cache": { + "title": "Statistiques de cache", + "cachename": "Nom du cache", + "hits": "Données existantes", + "misses": "Données non existantes", + "gets": "Lectures", + "puts": "Écritures", + "removals": "Suppressions", + "evictions": "Évictions", + "hitPercent": "% données existantes", + "missPercent": "% données non existantes", + "averageGetTime": "Temps de lecture moyen (µs)", + "averagePutTime": "Temps d'écriture moyen (µs)", + "averageRemoveTime": "Temps de suppression moyen (µs)" + }, + "datasource": { + "usage": "Usage du Pool de connexion", + "title": "Statistiques du pool de connexions (temps en millisecondes)", + "name": "Utilisation du pool", + "count": "Total", + "mean": "Médian", + "min": "Min", + "max": "Max", + "p50": "p50", + "p75": "p75", + "p95": "p95", + "p99": "p99" + } + } +} diff --git a/src/main/webapp/i18n/fr/password.json b/src/main/webapp/i18n/fr/password.json new file mode 100644 index 00000000..1c5856bc --- /dev/null +++ b/src/main/webapp/i18n/fr/password.json @@ -0,0 +1,12 @@ +{ + "password": { + "title": "Changer le mot de passe pour [{{username}}]", + "form": { + "button": "Sauvegarder" + }, + "messages": { + "error": "Une erreur est survenue ! Le mot de passe n'a pas pu être modifié.", + "success": "Le mot de passe a été modifié !" + } + } +} diff --git a/src/main/webapp/i18n/fr/points.json b/src/main/webapp/i18n/fr/points.json new file mode 100644 index 00000000..7da99a00 --- /dev/null +++ b/src/main/webapp/i18n/fr/points.json @@ -0,0 +1,30 @@ +{ + "twentyOnePointsApp": { + "points": { + "home": { + "title": "Points", + "refreshListLabel": "Actualiser la liste", + "createLabel": "Créer un nouveau Points", + "createOrEditLabel": "Créer ou éditer un Points", + "search": "Recherche pour Points", + "notFound": "Aucun Points trouvé" + }, + "created": "Un nouveau Points a été créé avec l'identifiant {{ param }}", + "updated": "Le Points avec l'identifiant {{ param }} a été mis à jour", + "deleted": "Le Points avec l'identifiant {{ param }} a été supprimé", + "delete": { + "question": "Êtes-vous certain de vouloir supprimer le Points {{ id }} ?" + }, + "detail": { + "title": "Points" + }, + "id": "ID", + "date": "Date", + "exercise": "Exercise", + "meals": "Meals", + "alcohol": "Alcohol", + "notes": "Notes", + "user": "User" + } + } +} diff --git a/src/main/webapp/i18n/fr/preferences.json b/src/main/webapp/i18n/fr/preferences.json new file mode 100644 index 00000000..092bc84e --- /dev/null +++ b/src/main/webapp/i18n/fr/preferences.json @@ -0,0 +1,27 @@ +{ + "twentyOnePointsApp": { + "preferences": { + "home": { + "title": "Preferences", + "refreshListLabel": "Actualiser la liste", + "createLabel": "Créer un nouveau Preferences", + "createOrEditLabel": "Créer ou éditer un Preferences", + "search": "Recherche pour Preferences", + "notFound": "Aucun Preferences trouvé" + }, + "created": "Un nouveau Preferences a été créé avec l'identifiant {{ param }}", + "updated": "Le Preferences avec l'identifiant {{ param }} a été mis à jour", + "deleted": "Le Preferences avec l'identifiant {{ param }} a été supprimé", + "delete": { + "question": "Êtes-vous certain de vouloir supprimer le Preferences {{ id }} ?" + }, + "detail": { + "title": "Preferences" + }, + "id": "ID", + "weeklyGoal": "Weekly Goal", + "weightUnits": "Weight Units", + "user": "User" + } + } +} diff --git a/src/main/webapp/i18n/fr/register.json b/src/main/webapp/i18n/fr/register.json new file mode 100644 index 00000000..08eb09db --- /dev/null +++ b/src/main/webapp/i18n/fr/register.json @@ -0,0 +1,24 @@ +{ + "register": { + "title": "Création de compte utilisateur", + "form": { + "button": "Enregistrement" + }, + "messages": { + "validate": { + "login": { + "required": "Votre nom d'utilisateur est obligatoire.", + "minlength": "Votre nom d'utilisateur doit contenir plus d'un caractère.", + "maxlength": "Votre nom d'utilisateur ne peut pas contenir plus de 50 caractères.", + "pattern": "Votre nom d'utilisateur est invalide." + } + }, + "success": "Compte enregistré ! Merci de vérifier votre email de confirmation.", + "error": { + "fail": "Compte non créé ! Merci d'essayer à nouveau plus tard.", + "userexists": "Ce compte utilisateur existe déjà ! Veuillez en choisir un autre.", + "emailexists": "Cet email est déjà utilisé ! Veuillez en choisir un autre." + } + } + } +} diff --git a/src/main/webapp/i18n/fr/reset.json b/src/main/webapp/i18n/fr/reset.json new file mode 100644 index 00000000..df7c18f7 --- /dev/null +++ b/src/main/webapp/i18n/fr/reset.json @@ -0,0 +1,26 @@ +{ + "reset": { + "request": { + "title": "Réinitialiser son mot de passe", + "form": { + "button": "Réinitialiser le mot de passe" + }, + "messages": { + "info": "Veuillez renseigner l'adresse email utilisée pour vous enregistrer", + "success": "Veuillez vérifier vos nouveaux emails et suivre les instructions pour réinitialiser votre mot de passe." + } + }, + "finish": { + "title": "Réinitialisation du mot de passe", + "form": { + "button": "Réinitialiser le mot de passe" + }, + "messages": { + "info": "Choisir un nouveau mot de passe", + "success": "Votre mot de passe a été réinitialisé. Merci de ", + "keymissing": "La clef de réinitilisation est manquante", + "error": "Votre mot de passe n'a pas pu être réinitialisé. La demande de réinitialisation n'est valable que 24 heures." + } + } + } +} diff --git a/src/main/webapp/i18n/fr/sessions.json b/src/main/webapp/i18n/fr/sessions.json new file mode 100644 index 00000000..1771ca41 --- /dev/null +++ b/src/main/webapp/i18n/fr/sessions.json @@ -0,0 +1,15 @@ +{ + "sessions": { + "title": "Sessions actives de [{{username}}]", + "table": { + "ipaddress": "Adresse IP", + "useragent": "User Agent", + "date": "Date", + "button": "Invalider" + }, + "messages": { + "success": "La session a été invalidée !", + "error": "Une erreur est survenue ! La session ne peut pas être invalidée." + } + } +} diff --git a/src/main/webapp/i18n/fr/settings.json b/src/main/webapp/i18n/fr/settings.json new file mode 100644 index 00000000..e9581dd0 --- /dev/null +++ b/src/main/webapp/i18n/fr/settings.json @@ -0,0 +1,32 @@ +{ + "settings": { + "title": "Profil de l'utilisateur [{{username}}]", + "form": { + "firstname": "Prénom", + "firstname.placeholder": "Votre prénom", + "lastname": "Nom", + "lastname.placeholder": "Votre nom", + "language": "Langue", + "button": "Sauvegarder" + }, + "messages": { + "error": { + "fail": "Une erreur est survenue ! Votre profil n'a pas été sauvegardé.", + "emailexists": "Cet email est déjà utilisé ! Veuillez en choisir un autre." + }, + "success": "Votre profil a été sauvegardé !", + "validate": { + "firstname": { + "required": "Votre prénom est requis.", + "minlength": "Votre prénom doit comporter au moins un caractère.", + "maxlength": "Votre prénom ne doit pas comporter plus de 50 caractères." + }, + "lastname": { + "required": "Votre nom est requis.", + "minlength": "Votre nom doit comporter au moins un caractère.", + "maxlength": "Votre nom ne doit pas comporter plus de 50 caractères." + } + } + } + } +} diff --git a/src/main/webapp/i18n/fr/units.json b/src/main/webapp/i18n/fr/units.json new file mode 100644 index 00000000..3298e8f6 --- /dev/null +++ b/src/main/webapp/i18n/fr/units.json @@ -0,0 +1,9 @@ +{ + "twentyOnePointsApp": { + "Units": { + "null": "", + "KG": "KG", + "LB": "LB" + } + } +} diff --git a/src/main/webapp/i18n/fr/user-management.json b/src/main/webapp/i18n/fr/user-management.json new file mode 100644 index 00000000..ac338c8d --- /dev/null +++ b/src/main/webapp/i18n/fr/user-management.json @@ -0,0 +1,31 @@ +{ + "userManagement": { + "home": { + "title": "Utilisateurs", + "refreshListLabel": "Actualiser la liste", + "createLabel": "Créer un nouvel utilisateur", + "createOrEditLabel": "Créer ou éditer un utilisateur" + }, + "created": "L'utilisateur {{ param }} a été créé.", + "updated": "L'utilisateur {{ param }} a été mis à jour.", + "deleted": "L'utilisateur {{ param }} a été supprimé.", + "delete": { + "question": "Etes-vous certain de vouloir supprimer l'utilisateur {{ login }} ?" + }, + "detail": { + "title": "Utilisateur" + }, + "login": "Login", + "firstName": "Prénom", + "lastName": "Nom", + "email": "Email", + "activated": "Activé", + "deactivated": "Désactivé", + "profiles": "Droits", + "langKey": "Langue", + "createdBy": "Créé par", + "createdDate": "Créé le", + "lastModifiedBy": "Modifié par", + "lastModifiedDate": "Modifié le" + } +} diff --git a/src/main/webapp/i18n/fr/weight.json b/src/main/webapp/i18n/fr/weight.json new file mode 100644 index 00000000..b23818b6 --- /dev/null +++ b/src/main/webapp/i18n/fr/weight.json @@ -0,0 +1,27 @@ +{ + "twentyOnePointsApp": { + "weight": { + "home": { + "title": "Weights", + "refreshListLabel": "Actualiser la liste", + "createLabel": "Créer un nouveau Weight", + "createOrEditLabel": "Créer ou éditer un Weight", + "search": "Recherche pour Weight", + "notFound": "Aucun Weight trouvé" + }, + "created": "Un nouveau Weight a été créé avec l'identifiant {{ param }}", + "updated": "Le Weight avec l'identifiant {{ param }} a été mis à jour", + "deleted": "Le Weight avec l'identifiant {{ param }} a été supprimé", + "delete": { + "question": "Êtes-vous certain de vouloir supprimer le Weight {{ id }} ?" + }, + "detail": { + "title": "Weight" + }, + "id": "ID", + "timestamp": "Timestamp", + "weight": "Weight", + "user": "User" + } + } +} diff --git a/src/main/webapp/index.html b/src/main/webapp/index.html new file mode 100644 index 00000000..23a5dc1d --- /dev/null +++ b/src/main/webapp/index.html @@ -0,0 +1,128 @@ + + + + + + TwentyOnePoints + + + + + + + + + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+ + + + + diff --git a/src/main/webapp/main.ts b/src/main/webapp/main.ts new file mode 100644 index 00000000..73c3a0e8 --- /dev/null +++ b/src/main/webapp/main.ts @@ -0,0 +1 @@ +import('./bootstrap').catch(err => console.error(err)); diff --git a/src/main/webapp/manifest.webapp b/src/main/webapp/manifest.webapp new file mode 100644 index 00000000..edccbcb5 --- /dev/null +++ b/src/main/webapp/manifest.webapp @@ -0,0 +1,31 @@ +{ + "name": "TwentyOnePoints", + "short_name": "TwentyOnePoints", + "icons": [ + { + "src": "./content/images/jhipster_family_member_0_head-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "./content/images/jhipster_family_member_0_head-256.png", + "sizes": "256x256", + "type": "image/png" + }, + { + "src": "./content/images/jhipster_family_member_0_head-384.png", + "sizes": "384x384", + "type": "image/png" + }, + { + "src": "./content/images/jhipster_family_member_0_head-512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "theme_color": "#000000", + "background_color": "#e0e0e0", + "start_url": ".", + "display": "standalone", + "orientation": "portrait" +} diff --git a/src/main/webapp/polyfills.ts b/src/main/webapp/polyfills.ts new file mode 100644 index 00000000..a144f00c --- /dev/null +++ b/src/main/webapp/polyfills.ts @@ -0,0 +1,5 @@ +import 'zone.js'; +import '@angular/localize/init'; + +// Fix needed for SockJS, see https://github.com/sockjs/sockjs-client/issues/439 +(window as any).global = window; diff --git a/src/main/webapp/robots.txt b/src/main/webapp/robots.txt new file mode 100644 index 00000000..361ff600 --- /dev/null +++ b/src/main/webapp/robots.txt @@ -0,0 +1,10 @@ +# robotstxt.org/ + +User-agent: * +Disallow: /api/account +Disallow: /api/account/change-password +Disallow: /api/account/sessions +Disallow: /api/logs/ +Disallow: /api/users/ +Disallow: /management/ +Disallow: /v3/api-docs/ diff --git a/src/main/webapp/swagger-ui/dist/images/throbber.gif b/src/main/webapp/swagger-ui/dist/images/throbber.gif new file mode 100644 index 0000000000000000000000000000000000000000..06393889242fb3ea9e0205fa84369ec7bb66d15a GIT binary patch literal 9257 zcmd^^X;@R|x`tQg5wbE8AV3mAn1TjmQ&en2CK8~ENEH<+P_)pZ24y2E+7O0>K^a6u zQ3;5MiU^7p6*M3qDk!2=YEcHMQ>nzEYP;R`e2C@r+U+?#XaC*&gKPcB#k$`o&;7mu zYNhYYXe|Uo84#4ZIko#rcU5K8*yFL{qT47O&^5fZH$ zVZ@%(l~vVHjnm;H@KL8@r%yUHoo;rbHI_4lIH(_nsTT>S2`DFOD~uCb9_dF4`#QgI zy7ldMcLs+A_s%|e1pRPrbX-tpeNP!9(IpMFTce`t_5U%lP99z%&i6`1d~ zWeM!Rxc50<+d$e^9LT`?B+aMK~apR zHm?q;p<7{wN2g|I^aGlSws;VP84j(z%aQwvAWv83Z$}p(% zZ^?2;gxg(ey_`V5J7{;!o;o;KslW@z5EP~JGs|U)J7dF&(ff#A=6vU?cGQ$-4+;Jf z-ggJEa!yStn`_EWvl)#yhm6XVs}UUbsi;+agri;mCfjH^Uy;lH+Zw^h)4N?oZgZz4 zJk(fTZ|Bi^;+s_M=~+d#vyoxEPzTlOS=mX@sbl*uRj>=MaMr}cFIY8i?UM61>86uB zV$DlOUCiUJwbzJMP@D$urzK|lL2-PC!p1l47V-ZG<5Ev0Z5h~Kx?`KOp7gkAjV93A z-Gc7MrlxTf?wF;CbNc@tCHJH{TB3c;#{SVu%97}tyAM2n&|9W_?qv}$*Jt*%7Yxb# zV0;d;7|lDEltJYS+U)#aiJO};?_Jyy_4%syQ(uy?-J-Yx-9O5nKRk@@XSS~X<(2u~ zV-LamWm~!iqtH9wkpf8mAXZhOD&L#aA_%)4h2M;1M5jt zIR>Us+%W-GXa_f^opKg=DSrAs)AXeRa;Hp0aC1OgbxQ%Qr_QvTleM1jkR!2mkcX$3 ztsR8~G9iqh(-FJ@F_rQBIYDXV_6s7G9SxaVF^laZqcx$!D97m|7t16j6@Jt6UdDRy49Qyvs|c>RuA|@b%}`*wU}2^7q;&Vtc6@lb zcXl)T!6nYDzmMJ~%n$KNXyNlCG)GkJ4!82;v6@d3>s5r~E+3!O?049JDr14Y^PeMI02R`0lJ^=oJ zYd|*u9|SU(j7hY?+<=(?fP*mtV*zFhOrz6%{VA?ozdm&(Jf^V zMfPZ?>l`mS3{Uq8IM;e!+1YjJy2!mzK$O|wPeU{*QSbs9m+@`f5KxO3PBnQ=%RsZg%go*fJ`*w9TL{-WgZVIA$!YV}3BRcfeXaR$x#b zW)Tpd#8E4)^MyYdkH;4_;ChJuw%n+Be7Ko4;w-nHvyo$d_0e-YiF78Df&)_)(}fcr_r0mPH(4RRYWIu+d@t0&Ss@O^s! zOKyX&13)%N@83r^;QsgN{rl(!0|RF1FA)b1{CRXAy&1ySz@>olPiR4r$aMdq&_=nK zq|cFs8phWJ1@%dZ-gXd{zDbTILD>)qEvH-NU*Rf1b2J1Ri79`rBFl@ z8E^0I)OqEi{pH(a24b9YPG;Kz@t-qZW;3Mpe`MRlmYx{7bH-XZ&`RQ7Rb^%}gc&X| zd}Q-FZf|RWxHU?PR!(C?80zu(^l>*h{#ulSiid(O!J(8P-41bNM3tnX@U6NS5yo0? zdcF)~xFE&+&|gZ$23dV5t~?$$&ymZ;F8j7GGMncGSsDo%>J`26=&l=X#rSKv_64;0 zr;k6no@=gV`P)K!=kaHl>q?!`X>(A;84tg^Md<`zA%qbRLby1Z=fn*ZRdNqs%Tq|3 zOt}lZu0q9oKJhgz&+^7PCt$=UFW=R*w?a1)ePoL*`R$Gxj?TU@12tTHsT$giHQU+sqf;fS0FpT!< z z#UR4L_rT;lfRLVo8|3$7cmuxwjY5rmYs&kR6z_LRhf9-=4QalKQYEWw^4-EBI3j$& zA>$Im_{ZA>0`)E_&m%x6a)BThkx=e|aMkOrK9zb1YzqpQ&WZ^$)2T>CwTCuYRn5y) z3fVXg-@R5&Bf4?WUTyD|hBDe2>xEh|o-y}o5Se~+Ob!5xN>CaAN!<4)F zwNh!Y7B?@AigokFYNJL`0Vz&-ekrY95-n3M<%GR<;SzXRmO7(zd+gf|$Thb%;pby2 zyd{5TJ?|JYUgpSlJ0=LB@k6#d&opuPGq^qJAIumfhigC2qAX0OEnYnT@O;bA?X1O5 zpLe9|%_H+Yki!Rv$7Kvjv8r7Z?$<>G)g*%D*V#s&kz>Z3V1 z3!ZKh9H8Nl9IdhEW_rY#oYdDCLTe+nQ{(d2pBX8%CmxL+1`|b#Vb!?IY!kT7$PDWAP9$FY=e9KSK{DEH|408! zl-$lv)U8$EB{~es&j>rYg%{{JRvIl8@NK}L=xDAEVv(o#W@3LUDc*m?yKSPR0O|nY zAh;*QuBdpja8HzP8Uw`ce-r*LrUA47ZvZ)ff3k4^>;dFcof}9eXeeM<0OVj&CKDVK zpUKKIF%hSmry!pwK68UX>zOF@dv}B4Gg)^2GQmN7@A?zG!xO6dT*Cq0+r{eY6}AfU zf`|~y!?^R*nB0!iTcg|CgM}ou^H*s~5)%h;Xh;PYOM!|Yhfk$w;@`1Dx1y!EZrM&^zMat!^Wz# z=Z{;Pa0w21oA1X3*9=`*c7o3ePa^k%Vzu>2C_7DaZJ8FW5GJv|t>`Ym;_S>7g_3XI zdRb!Ppd`ErK`pUDHRsJd9@)bu>}s1)nKsyAR7h21<1u{DX1gd_Vf;^zdUpFPeSHHR z7AMgw^{FlFlK91CGMafKt`$FLhq#^=->@Uok7pqW6&#Zs4*E(i5-jog43A*qC@!(8 z8&F}pofRcMVmcJd=f;fvlfAR!ZqeaTE?#TQ^jQM0ioaJf8m^!Kdv^`f5kEsD0=gX#4={QE1$3A4K~V$ITKEd){XVLx?i6K*D>JF6E=i znqF^X#&UX}rfB|#A9%y|sR5i6B5gyk>8@Q+xHg|^5iz7C2}YkGF)nuP4LX#k2tRBP z=!VnWnXea(K#Wvg2&0f{!mXuuWaPpsoZ)3TSaEp;i|_)CvP=4wjI; zH%7tcLM8dQXsHW*#|}%TG9yiGpyjBltpcpXkpl8zg~x zD{QG)2Z8x$vfjgDc(J6i|OHoLX&!<+m^<$S3DtA8Mf!{ z7;g1}0uqJ0Mxuy%=#BFX5;Xh9JkrA$d}neS9T;$F$kXn}ss zF{Jn}9EDk=>h)sMy$YXfhKIDxr7U@3xl+uI|N5y!>?{aVn703L1Qgb$ql%JT^lsGD%)~)(H?Spj$zNt)h)Raob z@KyVB@&ngE0rtMW4!UTqGX>{&KHJAWqb)oYq9O)e)nmN0jVa;LNbKXx04a+8&O;q) zHBzGejrqt7Dk$Z2VR%%K#`!((pXE*MR{jGtv|q$p5#v9N0f^6B9IB!Q6(y$TmHRLM zsYXm2jn3f{9T)KVVzotDx=Ng8q0Z*VDZOkd5C!p0PRoFt>NyVEc9*%YR&2>Nq~$AI zXOQfjJ&wpGMe~I8y=cC(QR4=W2GWccFK(3`d&gN+)qWtW-`*}mZI%KDRl4@rUv1%d zxFO82lhW$xQyYxJg8tOZyXm1As%kEFNn)eW{R61M>af@wr(YW{R@+eL2 zx?SovK+867$F%T;Dfeajw|kiQ81GcOnS$Y4+hp8g_w1P8_~79d9p$*M1_Ei81$H$Ti6oi?ZW)&tmsJa7RV1LKddm7R*qL54L7j zvCr1Mrb;l!=m^TbJun-C_6$7w81E1eAQC^6s4>rZ4&I5+yyu$kha%Z&d+|S7Ki#{2 zy}%Giz|eR|G?ychX%%=eL`W(aLarb(L4jd>J+wlX;xMV9H8J!l&i?~Mw7)jlIuLD% zyq+AK92j#kC`ycv$SJ|E7!FBParx#v<3_rZ-DLQ@>`#sdl5}immok8&`{YgF|+< z`tB>e%6G{=B4?V-be>`&*}0d*f?$yBX@w+rJht@O+=^zttqB2p=IiA17#YD$4-fih z@$gJ95mGmFhN!d;3Ag4#>3o`>%L{G=9<}qOJ$wDN)%)MN6bVsAPG4oKB3+8r6!Qf9 z3m8?jIpWcEJbt6|f?Y4nMXK(--YZ|GA2_aRS!do%J9S7?Q&4FYL@sPilq}e4tlYa& z?f+we^=FH^Z9|dnXZghblW!IYGIAT{``58&7vZBybh+GuIPP{h*J?&vf7i8rv6qgx zab9~l+K`tvC7pWtlS!5lt(n#Yl}PAR(v01oXjc0F?T0w>+*p#PtE?Tf_hMrEaZ!^V zbv_>=4xibc0TUxg^I>TS?HR4fdiWl`@6{7|WU9G68l7tOz2p>oIe~NNr!>Q&PHm`4 z98R?g(IT*nl#{_|*WO_h0X78;WwMp?A^Zi)W@BX5q==TdOl?~J6HK(0b(xD6?m3e3 z#+zMaSJb(W$h5+d+6vujSjyi_R80c9>7h;0YlUFDvN`iNGu&5HQ5^e>6x?&JSc4V$6_I1jJ4vnCVbkU`Gz=Uy#~OI( zlL-$UAE$pVCsD_rICM#Q!ltzcqDphp5L|ZrqUm>=H%x!RjMrF#*?BN2shvUg=H;)& zy~_xWl*k$~9Hl6PIq({dELPE-r4*YNs7?5{>dlC`EcK~lPKB_8V)G@H)UZFF8$tXT z@^raW#Hq4OJGFL2Aye|HU&_NL%dYans6?ltqEBz`Q|m=@Zh4=-p2r;}q(Nbsk$fUI zP|(Ns2>MDvZi1H7<55frlQn#%?`WY3g`+fRuC#UJx%#d!zxEu3=}zF514S=6f@?~$ zeuSB=6E7r3ya|; z@K7M3VBrls6c{M*M_{AB_fVjgQ|F(FuK(@=1eWeVMSpLglllqV6Rg-L_46;?^IskS z)x6|SR1^gGl6amWjkb1dX}^8DumNXNmhsfxKA#;bBBIZE@0gma5yQY(FX>|N~Y^mgq`xc zdxOf6r{9u#_e0gV3(fdBTdV2Sc4SN5ZmP?cB4?KR + + + + TwentyOnePoints - Swagger UI + + + + + + +
+ + + + + + + + diff --git a/src/test/java/org/jhipster/health/IntegrationTest.java b/src/test/java/org/jhipster/health/IntegrationTest.java new file mode 100644 index 00000000..04db9068 --- /dev/null +++ b/src/test/java/org/jhipster/health/IntegrationTest.java @@ -0,0 +1,24 @@ +package org.jhipster.health; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.jhipster.health.TwentyOnePointsApp; +import org.jhipster.health.config.AsyncSyncConfiguration; +import org.jhipster.health.config.EmbeddedElasticsearch; +import org.jhipster.health.config.EmbeddedSQL; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.annotation.DirtiesContext; + +/** + * Base composite annotation for integration tests. + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@SpringBootTest(classes = { TwentyOnePointsApp.class, AsyncSyncConfiguration.class }) +@EmbeddedElasticsearch +@EmbeddedSQL +@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS) +public @interface IntegrationTest { +} diff --git a/src/test/java/org/jhipster/health/TechnicalStructureTest.java b/src/test/java/org/jhipster/health/TechnicalStructureTest.java new file mode 100644 index 00000000..752425ee --- /dev/null +++ b/src/test/java/org/jhipster/health/TechnicalStructureTest.java @@ -0,0 +1,37 @@ +package org.jhipster.health; + +import static com.tngtech.archunit.base.DescribedPredicate.alwaysTrue; +import static com.tngtech.archunit.core.domain.JavaClass.Predicates.belongToAnyOf; +import static com.tngtech.archunit.library.Architectures.layeredArchitecture; + +import com.tngtech.archunit.core.importer.ImportOption.DoNotIncludeTests; +import com.tngtech.archunit.junit.AnalyzeClasses; +import com.tngtech.archunit.junit.ArchTest; +import com.tngtech.archunit.lang.ArchRule; + +@AnalyzeClasses(packagesOf = TwentyOnePointsApp.class, importOptions = DoNotIncludeTests.class) +class TechnicalStructureTest { + + // prettier-ignore + @ArchTest + static final ArchRule respectsTechnicalArchitectureLayers = layeredArchitecture() + .layer("Config").definedBy("..config..") + .layer("Web").definedBy("..web..") + .optionalLayer("Service").definedBy("..service..") + .layer("Security").definedBy("..security..") + .layer("Persistence").definedBy("..repository..") + .layer("Domain").definedBy("..domain..") + + .whereLayer("Config").mayNotBeAccessedByAnyLayer() + .whereLayer("Web").mayOnlyBeAccessedByLayers("Config") + .whereLayer("Service").mayOnlyBeAccessedByLayers("Web", "Config") + .whereLayer("Security").mayOnlyBeAccessedByLayers("Config", "Service", "Web") + .whereLayer("Persistence").mayOnlyBeAccessedByLayers("Service", "Security", "Web", "Config") + .whereLayer("Domain").mayOnlyBeAccessedByLayers("Persistence", "Service", "Security", "Web", "Config") + + .ignoreDependency(belongToAnyOf(TwentyOnePointsApp.class), alwaysTrue()) + .ignoreDependency(alwaysTrue(), belongToAnyOf( + org.jhipster.health.config.Constants.class, + org.jhipster.health.config.ApplicationProperties.class + )); +} diff --git a/src/test/java/org/jhipster/health/config/AsyncSyncConfiguration.java b/src/test/java/org/jhipster/health/config/AsyncSyncConfiguration.java new file mode 100644 index 00000000..0a03f478 --- /dev/null +++ b/src/test/java/org/jhipster/health/config/AsyncSyncConfiguration.java @@ -0,0 +1,16 @@ +package org.jhipster.health.config; + +import java.util.concurrent.Executor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Primary; +import org.springframework.core.task.SyncTaskExecutor; + +@Configuration +public class AsyncSyncConfiguration { + + @Bean(name = "taskExecutor") + public Executor taskExecutor() { + return new SyncTaskExecutor(); + } +} diff --git a/src/test/java/org/jhipster/health/config/ElasticsearchTestConfiguration.java b/src/test/java/org/jhipster/health/config/ElasticsearchTestConfiguration.java new file mode 100644 index 00000000..a27af056 --- /dev/null +++ b/src/test/java/org/jhipster/health/config/ElasticsearchTestConfiguration.java @@ -0,0 +1,19 @@ +package org.jhipster.health.config; + +import javax.annotation.PostConstruct; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate; +import org.springframework.data.elasticsearch.core.RefreshPolicy; + +@Configuration +public class ElasticsearchTestConfiguration { + + @Autowired + ElasticsearchRestTemplate template; + + @PostConstruct + public void configureTemplate() { + this.template.setRefreshPolicy(RefreshPolicy.IMMEDIATE); + } +} diff --git a/src/test/java/org/jhipster/health/config/ElasticsearchTestContainer.java b/src/test/java/org/jhipster/health/config/ElasticsearchTestContainer.java new file mode 100644 index 00000000..6cce012d --- /dev/null +++ b/src/test/java/org/jhipster/health/config/ElasticsearchTestContainer.java @@ -0,0 +1,49 @@ +package org.jhipster.health.config; + +import java.time.Duration; +import java.time.temporal.ChronoUnit; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.InitializingBean; +import org.testcontainers.containers.output.Slf4jLogConsumer; +import org.testcontainers.elasticsearch.ElasticsearchContainer; +import org.testcontainers.utility.DockerImageName; + +/** + * Base class for starting/stopping ElasticSearch during tests. + */ +public class ElasticsearchTestContainer implements InitializingBean, DisposableBean { + + private static final Logger log = LoggerFactory.getLogger(ElasticsearchTestContainer.class); + private static final Integer CONTAINER_STARTUP_TIMEOUT_MINUTES = 10; + private ElasticsearchContainer elasticsearchContainer; + + @Override + public void destroy() { + if (null != elasticsearchContainer && elasticsearchContainer.isRunning()) { + elasticsearchContainer.close(); + } + } + + @Override + public void afterPropertiesSet() { + if (null == elasticsearchContainer) { + elasticsearchContainer = + new ElasticsearchContainer(DockerImageName.parse("docker.elastic.co/elasticsearch/elasticsearch").withTag("7.17.4")) + .withStartupTimeout(Duration.of(CONTAINER_STARTUP_TIMEOUT_MINUTES, ChronoUnit.MINUTES)) + .withSharedMemorySize(256000000L) + .withEnv("ES_JAVA_OPTS", "-Xms256m -Xmx256m") + .withEnv("xpack.security.enabled", "false") + .withLogConsumer(new Slf4jLogConsumer(log)) + .withReuse(true); + } + if (!elasticsearchContainer.isRunning()) { + elasticsearchContainer.start(); + } + } + + public ElasticsearchContainer getElasticsearchContainer() { + return elasticsearchContainer; + } +} diff --git a/src/test/java/org/jhipster/health/config/EmbeddedElasticsearch.java b/src/test/java/org/jhipster/health/config/EmbeddedElasticsearch.java new file mode 100644 index 00000000..ea2d4705 --- /dev/null +++ b/src/test/java/org/jhipster/health/config/EmbeddedElasticsearch.java @@ -0,0 +1,11 @@ +package org.jhipster.health.config; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface EmbeddedElasticsearch { +} diff --git a/src/test/java/org/jhipster/health/config/EmbeddedSQL.java b/src/test/java/org/jhipster/health/config/EmbeddedSQL.java new file mode 100644 index 00000000..0c40cb05 --- /dev/null +++ b/src/test/java/org/jhipster/health/config/EmbeddedSQL.java @@ -0,0 +1,11 @@ +package org.jhipster.health.config; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface EmbeddedSQL { +} diff --git a/src/test/java/org/jhipster/health/config/PostgreSqlTestContainer.java b/src/test/java/org/jhipster/health/config/PostgreSqlTestContainer.java new file mode 100644 index 00000000..411e0044 --- /dev/null +++ b/src/test/java/org/jhipster/health/config/PostgreSqlTestContainer.java @@ -0,0 +1,42 @@ +package org.jhipster.health.config; + +import java.util.Collections; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testcontainers.containers.JdbcDatabaseContainer; +import org.testcontainers.containers.PostgreSQLContainer; +import org.testcontainers.containers.output.Slf4jLogConsumer; + +public class PostgreSqlTestContainer implements SqlTestContainer { + + private static final Logger log = LoggerFactory.getLogger(PostgreSqlTestContainer.class); + + private PostgreSQLContainer postgreSQLContainer; + + @Override + public void destroy() { + if (null != postgreSQLContainer && postgreSQLContainer.isRunning()) { + postgreSQLContainer.stop(); + } + } + + @Override + public void afterPropertiesSet() { + if (null == postgreSQLContainer) { + postgreSQLContainer = + new PostgreSQLContainer<>("postgres:14.5") + .withDatabaseName("TwentyOnePoints") + .withTmpFs(Collections.singletonMap("/testtmpfs", "rw")) + .withLogConsumer(new Slf4jLogConsumer(log)) + .withReuse(true); + } + if (!postgreSQLContainer.isRunning()) { + postgreSQLContainer.start(); + } + } + + @Override + public JdbcDatabaseContainer getTestContainer() { + return postgreSQLContainer; + } +} diff --git a/src/test/java/org/jhipster/health/config/SpringBootTestClassOrderer.java b/src/test/java/org/jhipster/health/config/SpringBootTestClassOrderer.java new file mode 100644 index 00000000..cc5ad211 --- /dev/null +++ b/src/test/java/org/jhipster/health/config/SpringBootTestClassOrderer.java @@ -0,0 +1,22 @@ +package org.jhipster.health.config; + +import java.util.Comparator; +import org.jhipster.health.IntegrationTest; +import org.junit.jupiter.api.ClassDescriptor; +import org.junit.jupiter.api.ClassOrderer; +import org.junit.jupiter.api.ClassOrdererContext; + +public class SpringBootTestClassOrderer implements ClassOrderer { + + @Override + public void orderClasses(ClassOrdererContext context) { + context.getClassDescriptors().sort(Comparator.comparingInt(SpringBootTestClassOrderer::getOrder)); + } + + private static int getOrder(ClassDescriptor classDescriptor) { + if (classDescriptor.findAnnotation(IntegrationTest.class).isPresent()) { + return 2; + } + return 1; + } +} diff --git a/src/test/java/org/jhipster/health/config/SqlTestContainer.java b/src/test/java/org/jhipster/health/config/SqlTestContainer.java new file mode 100644 index 00000000..3ed2a527 --- /dev/null +++ b/src/test/java/org/jhipster/health/config/SqlTestContainer.java @@ -0,0 +1,9 @@ +package org.jhipster.health.config; + +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.InitializingBean; +import org.testcontainers.containers.JdbcDatabaseContainer; + +public interface SqlTestContainer extends InitializingBean, DisposableBean { + JdbcDatabaseContainer getTestContainer(); +} diff --git a/src/test/java/org/jhipster/health/config/StaticResourcesWebConfigurerTest.java b/src/test/java/org/jhipster/health/config/StaticResourcesWebConfigurerTest.java new file mode 100644 index 00000000..82ed4021 --- /dev/null +++ b/src/test/java/org/jhipster/health/config/StaticResourcesWebConfigurerTest.java @@ -0,0 +1,76 @@ +package org.jhipster.health.config; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.jhipster.health.config.StaticResourcesWebConfiguration.*; +import static org.mockito.Mockito.*; + +import java.util.concurrent.TimeUnit; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.http.CacheControl; +import org.springframework.mock.web.MockServletContext; +import org.springframework.web.context.WebApplicationContext; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistration; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import tech.jhipster.config.JHipsterDefaults; +import tech.jhipster.config.JHipsterProperties; + +class StaticResourcesWebConfigurerTest { + + public static final int MAX_AGE_TEST = 5; + public StaticResourcesWebConfiguration staticResourcesWebConfiguration; + private ResourceHandlerRegistry resourceHandlerRegistry; + private MockServletContext servletContext; + private WebApplicationContext applicationContext; + private JHipsterProperties props; + + @BeforeEach + void setUp() { + servletContext = spy(new MockServletContext()); + applicationContext = mock(WebApplicationContext.class); + resourceHandlerRegistry = spy(new ResourceHandlerRegistry(applicationContext, servletContext)); + props = new JHipsterProperties(); + staticResourcesWebConfiguration = spy(new StaticResourcesWebConfiguration(props)); + } + + @Test + void shouldAppendResourceHandlerAndInitializeIt() { + staticResourcesWebConfiguration.addResourceHandlers(resourceHandlerRegistry); + + verify(resourceHandlerRegistry, times(1)).addResourceHandler(RESOURCE_PATHS); + verify(staticResourcesWebConfiguration, times(1)).initializeResourceHandler(any(ResourceHandlerRegistration.class)); + for (String testingPath : RESOURCE_PATHS) { + assertThat(resourceHandlerRegistry.hasMappingForPattern(testingPath)).isTrue(); + } + } + + @Test + void shouldInitializeResourceHandlerWithCacheControlAndLocations() { + CacheControl ccExpected = CacheControl.maxAge(5, TimeUnit.DAYS).cachePublic(); + when(staticResourcesWebConfiguration.getCacheControl()).thenReturn(ccExpected); + ResourceHandlerRegistration resourceHandlerRegistration = spy(new ResourceHandlerRegistration(RESOURCE_PATHS)); + + staticResourcesWebConfiguration.initializeResourceHandler(resourceHandlerRegistration); + + verify(staticResourcesWebConfiguration, times(1)).getCacheControl(); + verify(resourceHandlerRegistration, times(1)).setCacheControl(ccExpected); + verify(resourceHandlerRegistration, times(1)).addResourceLocations(RESOURCE_LOCATIONS); + } + + @Test + void shouldCreateCacheControlBasedOnJhipsterDefaultProperties() { + CacheControl cacheExpected = CacheControl.maxAge(JHipsterDefaults.Http.Cache.timeToLiveInDays, TimeUnit.DAYS).cachePublic(); + assertThat(staticResourcesWebConfiguration.getCacheControl()) + .extracting(CacheControl::getHeaderValue) + .isEqualTo(cacheExpected.getHeaderValue()); + } + + @Test + void shouldCreateCacheControlWithSpecificConfigurationInProperties() { + props.getHttp().getCache().setTimeToLiveInDays(MAX_AGE_TEST); + CacheControl cacheExpected = CacheControl.maxAge(MAX_AGE_TEST, TimeUnit.DAYS).cachePublic(); + assertThat(staticResourcesWebConfiguration.getCacheControl()) + .extracting(CacheControl::getHeaderValue) + .isEqualTo(cacheExpected.getHeaderValue()); + } +} diff --git a/src/test/java/org/jhipster/health/config/TestContainersSpringContextCustomizerFactory.java b/src/test/java/org/jhipster/health/config/TestContainersSpringContextCustomizerFactory.java new file mode 100644 index 00000000..c0698b32 --- /dev/null +++ b/src/test/java/org/jhipster/health/config/TestContainersSpringContextCustomizerFactory.java @@ -0,0 +1,75 @@ +package org.jhipster.health.config; + +import java.util.Arrays; +import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.beans.factory.support.DefaultSingletonBeanRegistry; +import org.springframework.boot.test.util.TestPropertyValues; +import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.test.context.ContextConfigurationAttributes; +import org.springframework.test.context.ContextCustomizer; +import org.springframework.test.context.ContextCustomizerFactory; +import tech.jhipster.config.JHipsterConstants; + +public class TestContainersSpringContextCustomizerFactory implements ContextCustomizerFactory { + + private Logger log = LoggerFactory.getLogger(TestContainersSpringContextCustomizerFactory.class); + + private static ElasticsearchTestContainer elasticsearchBean; + private static SqlTestContainer prodTestContainer; + + @Override + public ContextCustomizer createContextCustomizer(Class testClass, List configAttributes) { + return (context, mergedConfig) -> { + ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); + TestPropertyValues testValues = TestPropertyValues.empty(); + EmbeddedSQL sqlAnnotation = AnnotatedElementUtils.findMergedAnnotation(testClass, EmbeddedSQL.class); + if (null != sqlAnnotation) { + log.debug("detected the EmbeddedSQL annotation on class {}", testClass.getName()); + log.info("Warming up the sql database"); + if ( + Arrays + .asList(context.getEnvironment().getActiveProfiles()) + .contains("test" + JHipsterConstants.SPRING_PROFILE_PRODUCTION) + ) { + if (null == prodTestContainer) { + try { + Class containerClass = (Class) Class.forName( + this.getClass().getPackageName() + ".PostgreSqlTestContainer" + ); + prodTestContainer = beanFactory.createBean(containerClass); + beanFactory.registerSingleton(containerClass.getName(), prodTestContainer); + // ((DefaultListableBeanFactory)beanFactory).registerDisposableBean(containerClass.getName(), prodTestContainer); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + testValues = testValues.and("spring.datasource.url=" + prodTestContainer.getTestContainer().getJdbcUrl() + ""); + testValues = testValues.and("spring.datasource.username=" + prodTestContainer.getTestContainer().getUsername()); + testValues = testValues.and("spring.datasource.password=" + prodTestContainer.getTestContainer().getPassword()); + } + } + EmbeddedElasticsearch elasticsearchAnnotation = AnnotatedElementUtils.findMergedAnnotation( + testClass, + EmbeddedElasticsearch.class + ); + if (null != elasticsearchAnnotation) { + log.debug("detected the EmbeddedElasticsearch annotation on class {}", testClass.getName()); + log.info("Warming up the elastic database"); + if (null == elasticsearchBean) { + elasticsearchBean = beanFactory.createBean(ElasticsearchTestContainer.class); + beanFactory.registerSingleton(ElasticsearchTestContainer.class.getName(), elasticsearchBean); + // ((DefaultListableBeanFactory)beanFactory).registerDisposableBean(ElasticsearchTestContainer.class.getName(), elasticsearchBean); + } + testValues = + testValues.and( + "spring.elasticsearch.uris=http://" + elasticsearchBean.getElasticsearchContainer().getHttpHostAddress() + ); + } + testValues.applyTo(context); + }; + } +} diff --git a/src/test/java/org/jhipster/health/config/WebConfigurerTest.java b/src/test/java/org/jhipster/health/config/WebConfigurerTest.java new file mode 100644 index 00000000..5e18669c --- /dev/null +++ b/src/test/java/org/jhipster/health/config/WebConfigurerTest.java @@ -0,0 +1,133 @@ +package org.jhipster.health.config; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.options; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import java.io.File; +import java.util.*; +import javax.servlet.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.boot.web.embedded.undertow.UndertowServletWebServerFactory; +import org.springframework.http.HttpHeaders; +import org.springframework.mock.env.MockEnvironment; +import org.springframework.mock.web.MockServletContext; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import tech.jhipster.config.JHipsterConstants; +import tech.jhipster.config.JHipsterProperties; + +/** + * Unit tests for the {@link WebConfigurer} class. + */ +class WebConfigurerTest { + + private WebConfigurer webConfigurer; + + private MockServletContext servletContext; + + private MockEnvironment env; + + private JHipsterProperties props; + + @BeforeEach + public void setup() { + servletContext = spy(new MockServletContext()); + doReturn(mock(FilterRegistration.Dynamic.class)).when(servletContext).addFilter(anyString(), any(Filter.class)); + doReturn(mock(ServletRegistration.Dynamic.class)).when(servletContext).addServlet(anyString(), any(Servlet.class)); + + env = new MockEnvironment(); + props = new JHipsterProperties(); + + webConfigurer = new WebConfigurer(env, props); + } + + @Test + void shouldCustomizeServletContainer() { + env.setActiveProfiles(JHipsterConstants.SPRING_PROFILE_PRODUCTION); + UndertowServletWebServerFactory container = new UndertowServletWebServerFactory(); + webConfigurer.customize(container); + assertThat(container.getMimeMappings().get("abs")).isEqualTo("audio/x-mpeg"); + assertThat(container.getMimeMappings().get("html")).isEqualTo("text/html"); + assertThat(container.getMimeMappings().get("json")).isEqualTo("application/json"); + if (container.getDocumentRoot() != null) { + assertThat(container.getDocumentRoot()).isEqualTo(new File("build/resources/main/static/")); + } + } + + @Test + void shouldCorsFilterOnApiPath() throws Exception { + props.getCors().setAllowedOrigins(Collections.singletonList("other.domain.com")); + props.getCors().setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE")); + props.getCors().setAllowedHeaders(Collections.singletonList("*")); + props.getCors().setMaxAge(1800L); + props.getCors().setAllowCredentials(true); + + MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new WebConfigurerTestController()).addFilters(webConfigurer.corsFilter()).build(); + + mockMvc + .perform( + options("/api/test-cors") + .header(HttpHeaders.ORIGIN, "other.domain.com") + .header(HttpHeaders.ACCESS_CONTROL_REQUEST_METHOD, "POST") + ) + .andExpect(status().isOk()) + .andExpect(header().string(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, "other.domain.com")) + .andExpect(header().string(HttpHeaders.VARY, "Origin")) + .andExpect(header().string(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, "GET,POST,PUT,DELETE")) + .andExpect(header().string(HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true")) + .andExpect(header().string(HttpHeaders.ACCESS_CONTROL_MAX_AGE, "1800")); + + mockMvc + .perform(get("/api/test-cors").header(HttpHeaders.ORIGIN, "other.domain.com")) + .andExpect(status().isOk()) + .andExpect(header().string(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, "other.domain.com")); + } + + @Test + void shouldCorsFilterOnOtherPath() throws Exception { + props.getCors().setAllowedOrigins(Collections.singletonList("*")); + props.getCors().setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE")); + props.getCors().setAllowedHeaders(Collections.singletonList("*")); + props.getCors().setMaxAge(1800L); + props.getCors().setAllowCredentials(true); + + MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new WebConfigurerTestController()).addFilters(webConfigurer.corsFilter()).build(); + + mockMvc + .perform(get("/test/test-cors").header(HttpHeaders.ORIGIN, "other.domain.com")) + .andExpect(status().isOk()) + .andExpect(header().doesNotExist(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); + } + + @Test + void shouldCorsFilterDeactivatedForNullAllowedOrigins() throws Exception { + props.getCors().setAllowedOrigins(null); + + MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new WebConfigurerTestController()).addFilters(webConfigurer.corsFilter()).build(); + + mockMvc + .perform(get("/api/test-cors").header(HttpHeaders.ORIGIN, "other.domain.com")) + .andExpect(status().isOk()) + .andExpect(header().doesNotExist(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); + } + + @Test + void shouldCorsFilterDeactivatedForEmptyAllowedOrigins() throws Exception { + props.getCors().setAllowedOrigins(new ArrayList<>()); + + MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new WebConfigurerTestController()).addFilters(webConfigurer.corsFilter()).build(); + + mockMvc + .perform(get("/api/test-cors").header(HttpHeaders.ORIGIN, "other.domain.com")) + .andExpect(status().isOk()) + .andExpect(header().doesNotExist(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN)); + } +} diff --git a/src/test/java/org/jhipster/health/config/WebConfigurerTestController.java b/src/test/java/org/jhipster/health/config/WebConfigurerTestController.java new file mode 100644 index 00000000..5a5c39a4 --- /dev/null +++ b/src/test/java/org/jhipster/health/config/WebConfigurerTestController.java @@ -0,0 +1,14 @@ +package org.jhipster.health.config; + +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +public class WebConfigurerTestController { + + @GetMapping("/api/test-cors") + public void testCorsOnApiPath() {} + + @GetMapping("/test/test-cors") + public void testCorsOnOtherPath() {} +} diff --git a/src/test/java/org/jhipster/health/config/timezone/HibernateTimeZoneIT.java b/src/test/java/org/jhipster/health/config/timezone/HibernateTimeZoneIT.java new file mode 100644 index 00000000..e6579b64 --- /dev/null +++ b/src/test/java/org/jhipster/health/config/timezone/HibernateTimeZoneIT.java @@ -0,0 +1,162 @@ +package org.jhipster.health.config.timezone; + +import static java.lang.String.format; +import static org.assertj.core.api.Assertions.assertThat; + +import java.time.*; +import java.time.format.DateTimeFormatter; +import org.jhipster.health.IntegrationTest; +import org.jhipster.health.repository.timezone.DateTimeWrapper; +import org.jhipster.health.repository.timezone.DateTimeWrapperRepository; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.support.rowset.SqlRowSet; +import org.springframework.transaction.annotation.Transactional; + +/** + * Integration tests for the ZoneId Hibernate configuration. + */ +@IntegrationTest +class HibernateTimeZoneIT { + + @Autowired + private DateTimeWrapperRepository dateTimeWrapperRepository; + + @Autowired + private JdbcTemplate jdbcTemplate; + + @Value("${spring.jpa.properties.hibernate.jdbc.time_zone:UTC}") + private String zoneId; + + private DateTimeWrapper dateTimeWrapper; + private DateTimeFormatter dateTimeFormatter; + private DateTimeFormatter timeFormatter; + private DateTimeFormatter dateFormatter; + + @BeforeEach + public void setup() { + dateTimeWrapper = new DateTimeWrapper(); + dateTimeWrapper.setInstant(Instant.parse("2014-11-12T05:50:00.0Z")); + dateTimeWrapper.setLocalDateTime(LocalDateTime.parse("2014-11-12T07:50:00.0")); + dateTimeWrapper.setOffsetDateTime(OffsetDateTime.parse("2011-12-14T08:30:00.0Z")); + dateTimeWrapper.setZonedDateTime(ZonedDateTime.parse("2011-12-14T08:30:00.0Z")); + dateTimeWrapper.setLocalTime(LocalTime.parse("14:30:00")); + dateTimeWrapper.setOffsetTime(OffsetTime.parse("14:30:00+02:00")); + dateTimeWrapper.setLocalDate(LocalDate.parse("2016-09-10")); + + dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.S").withZone(ZoneId.of(zoneId)); + + timeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss").withZone(ZoneId.of(zoneId)); + + dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); + } + + @Test + @Transactional + void storeInstantWithZoneIdConfigShouldBeStoredOnGMTTimeZone() { + dateTimeWrapperRepository.saveAndFlush(dateTimeWrapper); + + String request = generateSqlRequest("instant", dateTimeWrapper.getId()); + SqlRowSet resultSet = jdbcTemplate.queryForRowSet(request); + String expectedValue = dateTimeFormatter.format(dateTimeWrapper.getInstant()); + + assertThatDateStoredValueIsEqualToInsertDateValueOnGMTTimeZone(resultSet, expectedValue); + } + + @Test + @Transactional + void storeLocalDateTimeWithZoneIdConfigShouldBeStoredOnGMTTimeZone() { + dateTimeWrapperRepository.saveAndFlush(dateTimeWrapper); + + String request = generateSqlRequest("local_date_time", dateTimeWrapper.getId()); + SqlRowSet resultSet = jdbcTemplate.queryForRowSet(request); + String expectedValue = dateTimeWrapper.getLocalDateTime().atZone(ZoneId.systemDefault()).format(dateTimeFormatter); + + assertThatDateStoredValueIsEqualToInsertDateValueOnGMTTimeZone(resultSet, expectedValue); + } + + @Test + @Transactional + void storeOffsetDateTimeWithZoneIdConfigShouldBeStoredOnGMTTimeZone() { + dateTimeWrapperRepository.saveAndFlush(dateTimeWrapper); + + String request = generateSqlRequest("offset_date_time", dateTimeWrapper.getId()); + SqlRowSet resultSet = jdbcTemplate.queryForRowSet(request); + String expectedValue = dateTimeWrapper.getOffsetDateTime().format(dateTimeFormatter); + + assertThatDateStoredValueIsEqualToInsertDateValueOnGMTTimeZone(resultSet, expectedValue); + } + + @Test + @Transactional + void storeZoneDateTimeWithZoneIdConfigShouldBeStoredOnGMTTimeZone() { + dateTimeWrapperRepository.saveAndFlush(dateTimeWrapper); + + String request = generateSqlRequest("zoned_date_time", dateTimeWrapper.getId()); + SqlRowSet resultSet = jdbcTemplate.queryForRowSet(request); + String expectedValue = dateTimeWrapper.getZonedDateTime().format(dateTimeFormatter); + + assertThatDateStoredValueIsEqualToInsertDateValueOnGMTTimeZone(resultSet, expectedValue); + } + + @Test + @Transactional + void storeLocalTimeWithZoneIdConfigShouldBeStoredOnGMTTimeZoneAccordingToHis1stJan1970Value() { + dateTimeWrapperRepository.saveAndFlush(dateTimeWrapper); + + String request = generateSqlRequest("local_time", dateTimeWrapper.getId()); + SqlRowSet resultSet = jdbcTemplate.queryForRowSet(request); + String expectedValue = dateTimeWrapper + .getLocalTime() + .atDate(LocalDate.of(1970, Month.JANUARY, 1)) + .atZone(ZoneId.systemDefault()) + .format(timeFormatter); + + assertThatDateStoredValueIsEqualToInsertDateValueOnGMTTimeZone(resultSet, expectedValue); + } + + @Test + @Transactional + void storeOffsetTimeWithZoneIdConfigShouldBeStoredOnGMTTimeZoneAccordingToHis1stJan1970Value() { + dateTimeWrapperRepository.saveAndFlush(dateTimeWrapper); + + String request = generateSqlRequest("offset_time", dateTimeWrapper.getId()); + SqlRowSet resultSet = jdbcTemplate.queryForRowSet(request); + String expectedValue = dateTimeWrapper + .getOffsetTime() + .toLocalTime() + .atDate(LocalDate.of(1970, Month.JANUARY, 1)) + .atZone(ZoneId.systemDefault()) + .format(timeFormatter); + + assertThatDateStoredValueIsEqualToInsertDateValueOnGMTTimeZone(resultSet, expectedValue); + } + + @Test + @Transactional + void storeLocalDateWithZoneIdConfigShouldBeStoredWithoutTransformation() { + dateTimeWrapperRepository.saveAndFlush(dateTimeWrapper); + + String request = generateSqlRequest("local_date", dateTimeWrapper.getId()); + SqlRowSet resultSet = jdbcTemplate.queryForRowSet(request); + String expectedValue = dateTimeWrapper.getLocalDate().format(dateFormatter); + + assertThatDateStoredValueIsEqualToInsertDateValueOnGMTTimeZone(resultSet, expectedValue); + } + + private String generateSqlRequest(String fieldName, long id) { + return format("SELECT %s FROM jhi_date_time_wrapper where id=%d", fieldName, id); + } + + private void assertThatDateStoredValueIsEqualToInsertDateValueOnGMTTimeZone(SqlRowSet sqlRowSet, String expectedValue) { + while (sqlRowSet.next()) { + String dbValue = sqlRowSet.getString(1); + + assertThat(dbValue).isNotNull(); + assertThat(dbValue).isEqualTo(expectedValue); + } + } +} diff --git a/src/test/java/org/jhipster/health/domain/BloodPressureTest.java b/src/test/java/org/jhipster/health/domain/BloodPressureTest.java new file mode 100644 index 00000000..aa9ed2cb --- /dev/null +++ b/src/test/java/org/jhipster/health/domain/BloodPressureTest.java @@ -0,0 +1,23 @@ +package org.jhipster.health.domain; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.jhipster.health.web.rest.TestUtil; +import org.junit.jupiter.api.Test; + +class BloodPressureTest { + + @Test + void equalsVerifier() throws Exception { + TestUtil.equalsVerifier(BloodPressure.class); + BloodPressure bloodPressure1 = new BloodPressure(); + bloodPressure1.setId(1L); + BloodPressure bloodPressure2 = new BloodPressure(); + bloodPressure2.setId(bloodPressure1.getId()); + assertThat(bloodPressure1).isEqualTo(bloodPressure2); + bloodPressure2.setId(2L); + assertThat(bloodPressure1).isNotEqualTo(bloodPressure2); + bloodPressure1.setId(null); + assertThat(bloodPressure1).isNotEqualTo(bloodPressure2); + } +} diff --git a/src/test/java/org/jhipster/health/domain/PointsTest.java b/src/test/java/org/jhipster/health/domain/PointsTest.java new file mode 100644 index 00000000..81164cd2 --- /dev/null +++ b/src/test/java/org/jhipster/health/domain/PointsTest.java @@ -0,0 +1,23 @@ +package org.jhipster.health.domain; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.jhipster.health.web.rest.TestUtil; +import org.junit.jupiter.api.Test; + +class PointsTest { + + @Test + void equalsVerifier() throws Exception { + TestUtil.equalsVerifier(Points.class); + Points points1 = new Points(); + points1.setId(1L); + Points points2 = new Points(); + points2.setId(points1.getId()); + assertThat(points1).isEqualTo(points2); + points2.setId(2L); + assertThat(points1).isNotEqualTo(points2); + points1.setId(null); + assertThat(points1).isNotEqualTo(points2); + } +} diff --git a/src/test/java/org/jhipster/health/domain/PreferencesTest.java b/src/test/java/org/jhipster/health/domain/PreferencesTest.java new file mode 100644 index 00000000..cb0e3483 --- /dev/null +++ b/src/test/java/org/jhipster/health/domain/PreferencesTest.java @@ -0,0 +1,23 @@ +package org.jhipster.health.domain; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.jhipster.health.web.rest.TestUtil; +import org.junit.jupiter.api.Test; + +class PreferencesTest { + + @Test + void equalsVerifier() throws Exception { + TestUtil.equalsVerifier(Preferences.class); + Preferences preferences1 = new Preferences(); + preferences1.setId(1L); + Preferences preferences2 = new Preferences(); + preferences2.setId(preferences1.getId()); + assertThat(preferences1).isEqualTo(preferences2); + preferences2.setId(2L); + assertThat(preferences1).isNotEqualTo(preferences2); + preferences1.setId(null); + assertThat(preferences1).isNotEqualTo(preferences2); + } +} diff --git a/src/test/java/org/jhipster/health/domain/WeightTest.java b/src/test/java/org/jhipster/health/domain/WeightTest.java new file mode 100644 index 00000000..05146de2 --- /dev/null +++ b/src/test/java/org/jhipster/health/domain/WeightTest.java @@ -0,0 +1,23 @@ +package org.jhipster.health.domain; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.jhipster.health.web.rest.TestUtil; +import org.junit.jupiter.api.Test; + +class WeightTest { + + @Test + void equalsVerifier() throws Exception { + TestUtil.equalsVerifier(Weight.class); + Weight weight1 = new Weight(); + weight1.setId(1L); + Weight weight2 = new Weight(); + weight2.setId(weight1.getId()); + assertThat(weight1).isEqualTo(weight2); + weight2.setId(2L); + assertThat(weight1).isNotEqualTo(weight2); + weight1.setId(null); + assertThat(weight1).isNotEqualTo(weight2); + } +} diff --git a/src/test/java/org/jhipster/health/management/SecurityMetersServiceTests.java b/src/test/java/org/jhipster/health/management/SecurityMetersServiceTests.java new file mode 100644 index 00000000..bad9d785 --- /dev/null +++ b/src/test/java/org/jhipster/health/management/SecurityMetersServiceTests.java @@ -0,0 +1,70 @@ +package org.jhipster.health.management; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; +import java.util.Collection; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class SecurityMetersServiceTests { + + private static final String INVALID_TOKENS_METER_EXPECTED_NAME = "security.authentication.invalid-tokens"; + + private MeterRegistry meterRegistry; + + private SecurityMetersService securityMetersService; + + @BeforeEach + public void setup() { + meterRegistry = new SimpleMeterRegistry(); + + securityMetersService = new SecurityMetersService(meterRegistry); + } + + @Test + void testInvalidTokensCountersByCauseAreCreated() { + meterRegistry.get(INVALID_TOKENS_METER_EXPECTED_NAME).counter(); + + meterRegistry.get(INVALID_TOKENS_METER_EXPECTED_NAME).tag("cause", "expired").counter(); + + meterRegistry.get(INVALID_TOKENS_METER_EXPECTED_NAME).tag("cause", "unsupported").counter(); + + meterRegistry.get(INVALID_TOKENS_METER_EXPECTED_NAME).tag("cause", "invalid-signature").counter(); + + meterRegistry.get(INVALID_TOKENS_METER_EXPECTED_NAME).tag("cause", "malformed").counter(); + + Collection counters = meterRegistry.find(INVALID_TOKENS_METER_EXPECTED_NAME).counters(); + + assertThat(counters).hasSize(4); + } + + @Test + void testCountMethodsShouldBeBoundToCorrectCounters() { + assertThat(meterRegistry.get(INVALID_TOKENS_METER_EXPECTED_NAME).tag("cause", "expired").counter().count()).isZero(); + + securityMetersService.trackTokenExpired(); + + assertThat(meterRegistry.get(INVALID_TOKENS_METER_EXPECTED_NAME).tag("cause", "expired").counter().count()).isEqualTo(1); + + assertThat(meterRegistry.get(INVALID_TOKENS_METER_EXPECTED_NAME).tag("cause", "unsupported").counter().count()).isZero(); + + securityMetersService.trackTokenUnsupported(); + + assertThat(meterRegistry.get(INVALID_TOKENS_METER_EXPECTED_NAME).tag("cause", "unsupported").counter().count()).isEqualTo(1); + + assertThat(meterRegistry.get(INVALID_TOKENS_METER_EXPECTED_NAME).tag("cause", "invalid-signature").counter().count()).isZero(); + + securityMetersService.trackTokenInvalidSignature(); + + assertThat(meterRegistry.get(INVALID_TOKENS_METER_EXPECTED_NAME).tag("cause", "invalid-signature").counter().count()).isEqualTo(1); + + assertThat(meterRegistry.get(INVALID_TOKENS_METER_EXPECTED_NAME).tag("cause", "malformed").counter().count()).isZero(); + + securityMetersService.trackTokenMalformed(); + + assertThat(meterRegistry.get(INVALID_TOKENS_METER_EXPECTED_NAME).tag("cause", "malformed").counter().count()).isEqualTo(1); + } +} diff --git a/src/test/java/org/jhipster/health/repository/timezone/DateTimeWrapper.java b/src/test/java/org/jhipster/health/repository/timezone/DateTimeWrapper.java new file mode 100644 index 00000000..e26b61b4 --- /dev/null +++ b/src/test/java/org/jhipster/health/repository/timezone/DateTimeWrapper.java @@ -0,0 +1,133 @@ +package org.jhipster.health.repository.timezone; + +import java.io.Serializable; +import java.time.*; +import java.util.Objects; +import javax.persistence.*; + +@Entity +@Table(name = "jhi_date_time_wrapper") +public class DateTimeWrapper implements Serializable { + + private static final long serialVersionUID = 1L; + + @Id + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator") + @SequenceGenerator(name = "sequenceGenerator") + private Long id; + + @Column(name = "instant") + private Instant instant; + + @Column(name = "local_date_time") + private LocalDateTime localDateTime; + + @Column(name = "offset_date_time") + private OffsetDateTime offsetDateTime; + + @Column(name = "zoned_date_time") + private ZonedDateTime zonedDateTime; + + @Column(name = "local_time") + private LocalTime localTime; + + @Column(name = "offset_time") + private OffsetTime offsetTime; + + @Column(name = "local_date") + private LocalDate localDate; + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public Instant getInstant() { + return instant; + } + + public void setInstant(Instant instant) { + this.instant = instant; + } + + public LocalDateTime getLocalDateTime() { + return localDateTime; + } + + public void setLocalDateTime(LocalDateTime localDateTime) { + this.localDateTime = localDateTime; + } + + public OffsetDateTime getOffsetDateTime() { + return offsetDateTime; + } + + public void setOffsetDateTime(OffsetDateTime offsetDateTime) { + this.offsetDateTime = offsetDateTime; + } + + public ZonedDateTime getZonedDateTime() { + return zonedDateTime; + } + + public void setZonedDateTime(ZonedDateTime zonedDateTime) { + this.zonedDateTime = zonedDateTime; + } + + public LocalTime getLocalTime() { + return localTime; + } + + public void setLocalTime(LocalTime localTime) { + this.localTime = localTime; + } + + public OffsetTime getOffsetTime() { + return offsetTime; + } + + public void setOffsetTime(OffsetTime offsetTime) { + this.offsetTime = offsetTime; + } + + public LocalDate getLocalDate() { + return localDate; + } + + public void setLocalDate(LocalDate localDate) { + this.localDate = localDate; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + DateTimeWrapper dateTimeWrapper = (DateTimeWrapper) o; + return !(dateTimeWrapper.getId() == null || getId() == null) && Objects.equals(getId(), dateTimeWrapper.getId()); + } + + @Override + public int hashCode() { + return Objects.hashCode(getId()); + } + + // prettier-ignore + @Override + public String toString() { + return "TimeZoneTest{" + + "id=" + id + + ", instant=" + instant + + ", localDateTime=" + localDateTime + + ", offsetDateTime=" + offsetDateTime + + ", zonedDateTime=" + zonedDateTime + + '}'; + } +} diff --git a/src/test/java/org/jhipster/health/repository/timezone/DateTimeWrapperRepository.java b/src/test/java/org/jhipster/health/repository/timezone/DateTimeWrapperRepository.java new file mode 100644 index 00000000..0a854c06 --- /dev/null +++ b/src/test/java/org/jhipster/health/repository/timezone/DateTimeWrapperRepository.java @@ -0,0 +1,10 @@ +package org.jhipster.health.repository.timezone; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +/** + * Spring Data JPA repository for the {@link DateTimeWrapper} entity. + */ +@Repository +public interface DateTimeWrapperRepository extends JpaRepository {} diff --git a/src/test/java/org/jhipster/health/security/DomainUserDetailsServiceIT.java b/src/test/java/org/jhipster/health/security/DomainUserDetailsServiceIT.java new file mode 100644 index 00000000..40ae5652 --- /dev/null +++ b/src/test/java/org/jhipster/health/security/DomainUserDetailsServiceIT.java @@ -0,0 +1,113 @@ +package org.jhipster.health.security; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; + +import java.util.Locale; +import org.apache.commons.lang3.RandomStringUtils; +import org.jhipster.health.IntegrationTest; +import org.jhipster.health.domain.User; +import org.jhipster.health.repository.UserRepository; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.transaction.annotation.Transactional; + +/** + * Integrations tests for {@link DomainUserDetailsService}. + */ +@Transactional +@IntegrationTest +class DomainUserDetailsServiceIT { + + private static final String USER_ONE_LOGIN = "test-user-one"; + private static final String USER_ONE_EMAIL = "test-user-one@localhost"; + private static final String USER_TWO_LOGIN = "test-user-two"; + private static final String USER_TWO_EMAIL = "test-user-two@localhost"; + private static final String USER_THREE_LOGIN = "test-user-three"; + private static final String USER_THREE_EMAIL = "test-user-three@localhost"; + + @Autowired + private UserRepository userRepository; + + @Autowired + @Qualifier("userDetailsService") + private UserDetailsService domainUserDetailsService; + + @BeforeEach + public void init() { + User userOne = new User(); + userOne.setLogin(USER_ONE_LOGIN); + userOne.setPassword(RandomStringUtils.randomAlphanumeric(60)); + userOne.setActivated(true); + userOne.setEmail(USER_ONE_EMAIL); + userOne.setFirstName("userOne"); + userOne.setLastName("doe"); + userOne.setLangKey("en"); + userRepository.save(userOne); + + User userTwo = new User(); + userTwo.setLogin(USER_TWO_LOGIN); + userTwo.setPassword(RandomStringUtils.randomAlphanumeric(60)); + userTwo.setActivated(true); + userTwo.setEmail(USER_TWO_EMAIL); + userTwo.setFirstName("userTwo"); + userTwo.setLastName("doe"); + userTwo.setLangKey("en"); + userRepository.save(userTwo); + + User userThree = new User(); + userThree.setLogin(USER_THREE_LOGIN); + userThree.setPassword(RandomStringUtils.randomAlphanumeric(60)); + userThree.setActivated(false); + userThree.setEmail(USER_THREE_EMAIL); + userThree.setFirstName("userThree"); + userThree.setLastName("doe"); + userThree.setLangKey("en"); + userRepository.save(userThree); + } + + @Test + void assertThatUserCanBeFoundByLogin() { + UserDetails userDetails = domainUserDetailsService.loadUserByUsername(USER_ONE_LOGIN); + assertThat(userDetails).isNotNull(); + assertThat(userDetails.getUsername()).isEqualTo(USER_ONE_LOGIN); + } + + @Test + void assertThatUserCanBeFoundByLoginIgnoreCase() { + UserDetails userDetails = domainUserDetailsService.loadUserByUsername(USER_ONE_LOGIN.toUpperCase(Locale.ENGLISH)); + assertThat(userDetails).isNotNull(); + assertThat(userDetails.getUsername()).isEqualTo(USER_ONE_LOGIN); + } + + @Test + void assertThatUserCanBeFoundByEmail() { + UserDetails userDetails = domainUserDetailsService.loadUserByUsername(USER_TWO_EMAIL); + assertThat(userDetails).isNotNull(); + assertThat(userDetails.getUsername()).isEqualTo(USER_TWO_LOGIN); + } + + @Test + void assertThatUserCanBeFoundByEmailIgnoreCase() { + UserDetails userDetails = domainUserDetailsService.loadUserByUsername(USER_TWO_EMAIL.toUpperCase(Locale.ENGLISH)); + assertThat(userDetails).isNotNull(); + assertThat(userDetails.getUsername()).isEqualTo(USER_TWO_LOGIN); + } + + @Test + void assertThatEmailIsPrioritizedOverLogin() { + UserDetails userDetails = domainUserDetailsService.loadUserByUsername(USER_ONE_EMAIL); + assertThat(userDetails).isNotNull(); + assertThat(userDetails.getUsername()).isEqualTo(USER_ONE_LOGIN); + } + + @Test + void assertThatUserNotActivatedExceptionIsThrownForNotActivatedUsers() { + assertThatExceptionOfType(UserNotActivatedException.class) + .isThrownBy(() -> domainUserDetailsService.loadUserByUsername(USER_THREE_LOGIN)); + } +} diff --git a/src/test/java/org/jhipster/health/security/SecurityUtilsUnitTest.java b/src/test/java/org/jhipster/health/security/SecurityUtilsUnitTest.java new file mode 100644 index 00000000..3ee3c815 --- /dev/null +++ b/src/test/java/org/jhipster/health/security/SecurityUtilsUnitTest.java @@ -0,0 +1,101 @@ +package org.jhipster.health.security; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Optional; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; + +/** + * Test class for the {@link SecurityUtils} utility class. + */ +class SecurityUtilsUnitTest { + + @BeforeEach + @AfterEach + void cleanup() { + SecurityContextHolder.clearContext(); + } + + @Test + void testGetCurrentUserLogin() { + SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); + securityContext.setAuthentication(new UsernamePasswordAuthenticationToken("admin", "admin")); + SecurityContextHolder.setContext(securityContext); + Optional login = SecurityUtils.getCurrentUserLogin(); + assertThat(login).contains("admin"); + } + + @Test + void testGetCurrentUserJWT() { + SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); + securityContext.setAuthentication(new UsernamePasswordAuthenticationToken("admin", "token")); + SecurityContextHolder.setContext(securityContext); + Optional jwt = SecurityUtils.getCurrentUserJWT(); + assertThat(jwt).contains("token"); + } + + @Test + void testIsAuthenticated() { + SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); + securityContext.setAuthentication(new UsernamePasswordAuthenticationToken("admin", "admin")); + SecurityContextHolder.setContext(securityContext); + boolean isAuthenticated = SecurityUtils.isAuthenticated(); + assertThat(isAuthenticated).isTrue(); + } + + @Test + void testAnonymousIsNotAuthenticated() { + SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); + Collection authorities = new ArrayList<>(); + authorities.add(new SimpleGrantedAuthority(AuthoritiesConstants.ANONYMOUS)); + securityContext.setAuthentication(new UsernamePasswordAuthenticationToken("anonymous", "anonymous", authorities)); + SecurityContextHolder.setContext(securityContext); + boolean isAuthenticated = SecurityUtils.isAuthenticated(); + assertThat(isAuthenticated).isFalse(); + } + + @Test + void testHasCurrentUserThisAuthority() { + SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); + Collection authorities = new ArrayList<>(); + authorities.add(new SimpleGrantedAuthority(AuthoritiesConstants.USER)); + securityContext.setAuthentication(new UsernamePasswordAuthenticationToken("user", "user", authorities)); + SecurityContextHolder.setContext(securityContext); + + assertThat(SecurityUtils.hasCurrentUserThisAuthority(AuthoritiesConstants.USER)).isTrue(); + assertThat(SecurityUtils.hasCurrentUserThisAuthority(AuthoritiesConstants.ADMIN)).isFalse(); + } + + @Test + void testHasCurrentUserAnyOfAuthorities() { + SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); + Collection authorities = new ArrayList<>(); + authorities.add(new SimpleGrantedAuthority(AuthoritiesConstants.USER)); + securityContext.setAuthentication(new UsernamePasswordAuthenticationToken("user", "user", authorities)); + SecurityContextHolder.setContext(securityContext); + + assertThat(SecurityUtils.hasCurrentUserAnyOfAuthorities(AuthoritiesConstants.USER, AuthoritiesConstants.ADMIN)).isTrue(); + assertThat(SecurityUtils.hasCurrentUserAnyOfAuthorities(AuthoritiesConstants.ANONYMOUS, AuthoritiesConstants.ADMIN)).isFalse(); + } + + @Test + void testHasCurrentUserNoneOfAuthorities() { + SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); + Collection authorities = new ArrayList<>(); + authorities.add(new SimpleGrantedAuthority(AuthoritiesConstants.USER)); + securityContext.setAuthentication(new UsernamePasswordAuthenticationToken("user", "user", authorities)); + SecurityContextHolder.setContext(securityContext); + + assertThat(SecurityUtils.hasCurrentUserNoneOfAuthorities(AuthoritiesConstants.USER, AuthoritiesConstants.ADMIN)).isFalse(); + assertThat(SecurityUtils.hasCurrentUserNoneOfAuthorities(AuthoritiesConstants.ANONYMOUS, AuthoritiesConstants.ADMIN)).isTrue(); + } +} diff --git a/src/test/java/org/jhipster/health/security/jwt/JWTFilterTest.java b/src/test/java/org/jhipster/health/security/jwt/JWTFilterTest.java new file mode 100644 index 00000000..c7518e35 --- /dev/null +++ b/src/test/java/org/jhipster/health/security/jwt/JWTFilterTest.java @@ -0,0 +1,117 @@ +package org.jhipster.health.security.jwt; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.jsonwebtoken.io.Decoders; +import io.jsonwebtoken.security.Keys; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; +import java.util.Collections; +import org.jhipster.health.management.SecurityMetersService; +import org.jhipster.health.security.AuthoritiesConstants; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpStatus; +import org.springframework.mock.web.MockFilterChain; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.mock.web.MockHttpServletResponse; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.test.util.ReflectionTestUtils; +import tech.jhipster.config.JHipsterProperties; + +class JWTFilterTest { + + private TokenProvider tokenProvider; + + private JWTFilter jwtFilter; + + @BeforeEach + public void setup() { + JHipsterProperties jHipsterProperties = new JHipsterProperties(); + String base64Secret = "fd54a45s65fds737b9aafcb3412e07ed99b267f33413274720ddbb7f6c5e64e9f14075f2d7ed041592f0b7657baf8"; + jHipsterProperties.getSecurity().getAuthentication().getJwt().setBase64Secret(base64Secret); + + SecurityMetersService securityMetersService = new SecurityMetersService(new SimpleMeterRegistry()); + + tokenProvider = new TokenProvider(jHipsterProperties, securityMetersService); + ReflectionTestUtils.setField(tokenProvider, "key", Keys.hmacShaKeyFor(Decoders.BASE64.decode(base64Secret))); + + ReflectionTestUtils.setField(tokenProvider, "tokenValidityInMilliseconds", 60000); + jwtFilter = new JWTFilter(tokenProvider); + SecurityContextHolder.getContext().setAuthentication(null); + } + + @Test + void testJWTFilter() throws Exception { + UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken( + "test-user", + "test-password", + Collections.singletonList(new SimpleGrantedAuthority(AuthoritiesConstants.USER)) + ); + String jwt = tokenProvider.createToken(authentication, false); + MockHttpServletRequest request = new MockHttpServletRequest(); + request.addHeader(JWTFilter.AUTHORIZATION_HEADER, "Bearer " + jwt); + request.setRequestURI("/api/test"); + MockHttpServletResponse response = new MockHttpServletResponse(); + MockFilterChain filterChain = new MockFilterChain(); + jwtFilter.doFilter(request, response, filterChain); + assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value()); + assertThat(SecurityContextHolder.getContext().getAuthentication().getName()).isEqualTo("test-user"); + assertThat(SecurityContextHolder.getContext().getAuthentication().getCredentials()).hasToString(jwt); + } + + @Test + void testJWTFilterInvalidToken() throws Exception { + String jwt = "wrong_jwt"; + MockHttpServletRequest request = new MockHttpServletRequest(); + request.addHeader(JWTFilter.AUTHORIZATION_HEADER, "Bearer " + jwt); + request.setRequestURI("/api/test"); + MockHttpServletResponse response = new MockHttpServletResponse(); + MockFilterChain filterChain = new MockFilterChain(); + jwtFilter.doFilter(request, response, filterChain); + assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value()); + assertThat(SecurityContextHolder.getContext().getAuthentication()).isNull(); + } + + @Test + void testJWTFilterMissingAuthorization() throws Exception { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setRequestURI("/api/test"); + MockHttpServletResponse response = new MockHttpServletResponse(); + MockFilterChain filterChain = new MockFilterChain(); + jwtFilter.doFilter(request, response, filterChain); + assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value()); + assertThat(SecurityContextHolder.getContext().getAuthentication()).isNull(); + } + + @Test + void testJWTFilterMissingToken() throws Exception { + MockHttpServletRequest request = new MockHttpServletRequest(); + request.addHeader(JWTFilter.AUTHORIZATION_HEADER, "Bearer "); + request.setRequestURI("/api/test"); + MockHttpServletResponse response = new MockHttpServletResponse(); + MockFilterChain filterChain = new MockFilterChain(); + jwtFilter.doFilter(request, response, filterChain); + assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value()); + assertThat(SecurityContextHolder.getContext().getAuthentication()).isNull(); + } + + @Test + void testJWTFilterWrongScheme() throws Exception { + UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken( + "test-user", + "test-password", + Collections.singletonList(new SimpleGrantedAuthority(AuthoritiesConstants.USER)) + ); + String jwt = tokenProvider.createToken(authentication, false); + MockHttpServletRequest request = new MockHttpServletRequest(); + request.addHeader(JWTFilter.AUTHORIZATION_HEADER, "Basic " + jwt); + request.setRequestURI("/api/test"); + MockHttpServletResponse response = new MockHttpServletResponse(); + MockFilterChain filterChain = new MockFilterChain(); + jwtFilter.doFilter(request, response, filterChain); + assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value()); + assertThat(SecurityContextHolder.getContext().getAuthentication()).isNull(); + } +} diff --git a/src/test/java/org/jhipster/health/security/jwt/TokenProviderSecurityMetersTests.java b/src/test/java/org/jhipster/health/security/jwt/TokenProviderSecurityMetersTests.java new file mode 100644 index 00000000..170bab3a --- /dev/null +++ b/src/test/java/org/jhipster/health/security/jwt/TokenProviderSecurityMetersTests.java @@ -0,0 +1,158 @@ +package org.jhipster.health.security.jwt; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import io.jsonwebtoken.io.Decoders; +import io.jsonwebtoken.security.Keys; +import io.micrometer.core.instrument.Counter; +import io.micrometer.core.instrument.MeterRegistry; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; +import java.security.Key; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import org.jhipster.health.management.SecurityMetersService; +import org.jhipster.health.security.AuthoritiesConstants; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.test.util.ReflectionTestUtils; +import tech.jhipster.config.JHipsterProperties; + +class TokenProviderSecurityMetersTests { + + private static final long ONE_MINUTE = 60000; + private static final String INVALID_TOKENS_METER_EXPECTED_NAME = "security.authentication.invalid-tokens"; + + private MeterRegistry meterRegistry; + + private TokenProvider tokenProvider; + + @BeforeEach + public void setup() { + JHipsterProperties jHipsterProperties = new JHipsterProperties(); + String base64Secret = "fd54a45s65fds737b9aafcb3412e07ed99b267f33413274720ddbb7f6c5e64e9f14075f2d7ed041592f0b7657baf8"; + jHipsterProperties.getSecurity().getAuthentication().getJwt().setBase64Secret(base64Secret); + + meterRegistry = new SimpleMeterRegistry(); + + SecurityMetersService securityMetersService = new SecurityMetersService(meterRegistry); + + tokenProvider = new TokenProvider(jHipsterProperties, securityMetersService); + Key key = Keys.hmacShaKeyFor(Decoders.BASE64.decode(base64Secret)); + + ReflectionTestUtils.setField(tokenProvider, "key", key); + ReflectionTestUtils.setField(tokenProvider, "tokenValidityInMilliseconds", ONE_MINUTE); + } + + @Test + void testValidTokenShouldNotCountAnything() { + Collection counters = meterRegistry.find(INVALID_TOKENS_METER_EXPECTED_NAME).counters(); + + assertThat(aggregate(counters)).isZero(); + + String validToken = createValidToken(); + + tokenProvider.validateToken(validToken); + + assertThat(aggregate(counters)).isZero(); + } + + @Test + void testTokenExpiredCount() { + assertThat(meterRegistry.get(INVALID_TOKENS_METER_EXPECTED_NAME).tag("cause", "expired").counter().count()).isZero(); + + String expiredToken = createExpiredToken(); + + tokenProvider.validateToken(expiredToken); + + assertThat(meterRegistry.get(INVALID_TOKENS_METER_EXPECTED_NAME).tag("cause", "expired").counter().count()).isEqualTo(1); + } + + @Test + void testTokenUnsupportedCount() { + assertThat(meterRegistry.get(INVALID_TOKENS_METER_EXPECTED_NAME).tag("cause", "unsupported").counter().count()).isZero(); + + String unsupportedToken = createUnsupportedToken(); + + tokenProvider.validateToken(unsupportedToken); + + assertThat(meterRegistry.get(INVALID_TOKENS_METER_EXPECTED_NAME).tag("cause", "unsupported").counter().count()).isEqualTo(1); + } + + @Test + void testTokenSignatureInvalidCount() { + assertThat(meterRegistry.get(INVALID_TOKENS_METER_EXPECTED_NAME).tag("cause", "invalid-signature").counter().count()).isZero(); + + String tokenWithDifferentSignature = createTokenWithDifferentSignature(); + + tokenProvider.validateToken(tokenWithDifferentSignature); + + assertThat(meterRegistry.get(INVALID_TOKENS_METER_EXPECTED_NAME).tag("cause", "invalid-signature").counter().count()).isEqualTo(1); + } + + @Test + void testTokenMalformedCount() { + assertThat(meterRegistry.get(INVALID_TOKENS_METER_EXPECTED_NAME).tag("cause", "malformed").counter().count()).isZero(); + + String malformedToken = createMalformedToken(); + + tokenProvider.validateToken(malformedToken); + + assertThat(meterRegistry.get(INVALID_TOKENS_METER_EXPECTED_NAME).tag("cause", "malformed").counter().count()).isEqualTo(1); + } + + private String createValidToken() { + Authentication authentication = createAuthentication(); + + return tokenProvider.createToken(authentication, false); + } + + private String createExpiredToken() { + ReflectionTestUtils.setField(tokenProvider, "tokenValidityInMilliseconds", -ONE_MINUTE); + + Authentication authentication = createAuthentication(); + + return tokenProvider.createToken(authentication, false); + } + + private Authentication createAuthentication() { + Collection authorities = new ArrayList<>(); + authorities.add(new SimpleGrantedAuthority(AuthoritiesConstants.ANONYMOUS)); + return new UsernamePasswordAuthenticationToken("anonymous", "anonymous", authorities); + } + + private String createUnsupportedToken() { + Key key = (Key) ReflectionTestUtils.getField(tokenProvider, "key"); + + return Jwts.builder().setPayload("payload").signWith(key, SignatureAlgorithm.HS256).compact(); + } + + private String createMalformedToken() { + String validToken = createValidToken(); + + return "X" + validToken; + } + + private String createTokenWithDifferentSignature() { + Key otherKey = Keys.hmacShaKeyFor( + Decoders.BASE64.decode("Xfd54a45s65fds737b9aafcb3412e07ed99b267f33413274720ddbb7f6c5e64e9f14075f2d7ed041592f0b7657baf8") + ); + + return Jwts + .builder() + .setSubject("anonymous") + .signWith(otherKey, SignatureAlgorithm.HS512) + .setExpiration(new Date(new Date().getTime() + ONE_MINUTE)) + .compact(); + } + + private double aggregate(Collection counters) { + return counters.stream().mapToDouble(Counter::count).sum(); + } +} diff --git a/src/test/java/org/jhipster/health/security/jwt/TokenProviderTest.java b/src/test/java/org/jhipster/health/security/jwt/TokenProviderTest.java new file mode 100644 index 00000000..ef05678b --- /dev/null +++ b/src/test/java/org/jhipster/health/security/jwt/TokenProviderTest.java @@ -0,0 +1,141 @@ +package org.jhipster.health.security.jwt; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.SignatureAlgorithm; +import io.jsonwebtoken.io.Decoders; +import io.jsonwebtoken.security.Keys; +import io.micrometer.core.instrument.simple.SimpleMeterRegistry; +import java.nio.charset.StandardCharsets; +import java.security.Key; +import java.util.*; +import org.jhipster.health.management.SecurityMetersService; +import org.jhipster.health.security.AuthoritiesConstants; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.test.util.ReflectionTestUtils; +import tech.jhipster.config.JHipsterProperties; + +class TokenProviderTest { + + private static final long ONE_MINUTE = 60000; + + private Key key; + private TokenProvider tokenProvider; + + @BeforeEach + public void setup() { + JHipsterProperties jHipsterProperties = new JHipsterProperties(); + String base64Secret = "fd54a45s65fds737b9aafcb3412e07ed99b267f33413274720ddbb7f6c5e64e9f14075f2d7ed041592f0b7657baf8"; + jHipsterProperties.getSecurity().getAuthentication().getJwt().setBase64Secret(base64Secret); + + SecurityMetersService securityMetersService = new SecurityMetersService(new SimpleMeterRegistry()); + + tokenProvider = new TokenProvider(jHipsterProperties, securityMetersService); + key = Keys.hmacShaKeyFor(Decoders.BASE64.decode(base64Secret)); + + ReflectionTestUtils.setField(tokenProvider, "key", key); + ReflectionTestUtils.setField(tokenProvider, "tokenValidityInMilliseconds", ONE_MINUTE); + } + + @Test + void testReturnFalseWhenJWThasInvalidSignature() { + boolean isTokenValid = tokenProvider.validateToken(createTokenWithDifferentSignature()); + + assertThat(isTokenValid).isFalse(); + } + + @Test + void testReturnFalseWhenJWTisMalformed() { + Authentication authentication = createAuthentication(); + String token = tokenProvider.createToken(authentication, false); + String invalidToken = token.substring(1); + boolean isTokenValid = tokenProvider.validateToken(invalidToken); + + assertThat(isTokenValid).isFalse(); + } + + @Test + void testReturnFalseWhenJWTisExpired() { + ReflectionTestUtils.setField(tokenProvider, "tokenValidityInMilliseconds", -ONE_MINUTE); + + Authentication authentication = createAuthentication(); + String token = tokenProvider.createToken(authentication, false); + + boolean isTokenValid = tokenProvider.validateToken(token); + + assertThat(isTokenValid).isFalse(); + } + + @Test + void testReturnFalseWhenJWTisUnsupported() { + String unsupportedToken = createUnsupportedToken(); + + boolean isTokenValid = tokenProvider.validateToken(unsupportedToken); + + assertThat(isTokenValid).isFalse(); + } + + @Test + void testReturnFalseWhenJWTisInvalid() { + boolean isTokenValid = tokenProvider.validateToken(""); + + assertThat(isTokenValid).isFalse(); + } + + @Test + void testKeyIsSetFromSecretWhenSecretIsNotEmpty() { + final String secret = "NwskoUmKHZtzGRKJKVjsJF7BtQMMxNWi"; + JHipsterProperties jHipsterProperties = new JHipsterProperties(); + jHipsterProperties.getSecurity().getAuthentication().getJwt().setSecret(secret); + + SecurityMetersService securityMetersService = new SecurityMetersService(new SimpleMeterRegistry()); + + TokenProvider tokenProvider = new TokenProvider(jHipsterProperties, securityMetersService); + + Key key = (Key) ReflectionTestUtils.getField(tokenProvider, "key"); + assertThat(key).isNotNull().isEqualTo(Keys.hmacShaKeyFor(secret.getBytes(StandardCharsets.UTF_8))); + } + + @Test + void testKeyIsSetFromBase64SecretWhenSecretIsEmpty() { + final String base64Secret = "fd54a45s65fds737b9aafcb3412e07ed99b267f33413274720ddbb7f6c5e64e9f14075f2d7ed041592f0b7657baf8"; + JHipsterProperties jHipsterProperties = new JHipsterProperties(); + jHipsterProperties.getSecurity().getAuthentication().getJwt().setBase64Secret(base64Secret); + + SecurityMetersService securityMetersService = new SecurityMetersService(new SimpleMeterRegistry()); + + TokenProvider tokenProvider = new TokenProvider(jHipsterProperties, securityMetersService); + + Key key = (Key) ReflectionTestUtils.getField(tokenProvider, "key"); + assertThat(key).isNotNull().isEqualTo(Keys.hmacShaKeyFor(Decoders.BASE64.decode(base64Secret))); + } + + private Authentication createAuthentication() { + Collection authorities = new ArrayList<>(); + authorities.add(new SimpleGrantedAuthority(AuthoritiesConstants.ANONYMOUS)); + return new UsernamePasswordAuthenticationToken("anonymous", "anonymous", authorities); + } + + private String createUnsupportedToken() { + return Jwts.builder().setPayload("payload").signWith(key, SignatureAlgorithm.HS512).compact(); + } + + private String createTokenWithDifferentSignature() { + Key otherKey = Keys.hmacShaKeyFor( + Decoders.BASE64.decode("Xfd54a45s65fds737b9aafcb3412e07ed99b267f33413274720ddbb7f6c5e64e9f14075f2d7ed041592f0b7657baf8") + ); + + return Jwts + .builder() + .setSubject("anonymous") + .signWith(otherKey, SignatureAlgorithm.HS512) + .setExpiration(new Date(new Date().getTime() + ONE_MINUTE)) + .compact(); + } +} diff --git a/src/test/java/org/jhipster/health/service/MailServiceIT.java b/src/test/java/org/jhipster/health/service/MailServiceIT.java new file mode 100644 index 00000000..370c5b4a --- /dev/null +++ b/src/test/java/org/jhipster/health/service/MailServiceIT.java @@ -0,0 +1,237 @@ +package org.jhipster.health.service; + +import static org.assertj.core.api.Assertions.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStreamReader; +import java.net.URI; +import java.net.URL; +import java.nio.charset.Charset; +import java.util.Properties; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.mail.Multipart; +import javax.mail.Session; +import javax.mail.internet.MimeBodyPart; +import javax.mail.internet.MimeMessage; +import javax.mail.internet.MimeMultipart; +import org.jhipster.health.IntegrationTest; +import org.jhipster.health.config.Constants; +import org.jhipster.health.domain.User; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.mail.MailSendException; +import org.springframework.mail.javamail.JavaMailSender; +import tech.jhipster.config.JHipsterProperties; + +/** + * Integration tests for {@link MailService}. + */ +@IntegrationTest +class MailServiceIT { + + private static final String[] languages = { + "en", + "fr", + // jhipster-needle-i18n-language-constant - JHipster will add/remove languages in this array + }; + private static final Pattern PATTERN_LOCALE_3 = Pattern.compile("([a-z]{2})-([a-zA-Z]{4})-([a-z]{2})"); + private static final Pattern PATTERN_LOCALE_2 = Pattern.compile("([a-z]{2})-([a-z]{2})"); + + @Autowired + private JHipsterProperties jHipsterProperties; + + @MockBean + private JavaMailSender javaMailSender; + + @Captor + private ArgumentCaptor messageCaptor; + + @Autowired + private MailService mailService; + + @BeforeEach + public void setup() { + doNothing().when(javaMailSender).send(any(MimeMessage.class)); + when(javaMailSender.createMimeMessage()).thenReturn(new MimeMessage((Session) null)); + } + + @Test + void testSendEmail() throws Exception { + mailService.sendEmail("john.doe@example.com", "testSubject", "testContent", false, false); + verify(javaMailSender).send(messageCaptor.capture()); + MimeMessage message = messageCaptor.getValue(); + assertThat(message.getSubject()).isEqualTo("testSubject"); + assertThat(message.getAllRecipients()[0]).hasToString("john.doe@example.com"); + assertThat(message.getFrom()[0]).hasToString(jHipsterProperties.getMail().getFrom()); + assertThat(message.getContent()).isInstanceOf(String.class); + assertThat(message.getContent()).hasToString("testContent"); + assertThat(message.getDataHandler().getContentType()).isEqualTo("text/plain; charset=UTF-8"); + } + + @Test + void testSendHtmlEmail() throws Exception { + mailService.sendEmail("john.doe@example.com", "testSubject", "testContent", false, true); + verify(javaMailSender).send(messageCaptor.capture()); + MimeMessage message = messageCaptor.getValue(); + assertThat(message.getSubject()).isEqualTo("testSubject"); + assertThat(message.getAllRecipients()[0]).hasToString("john.doe@example.com"); + assertThat(message.getFrom()[0]).hasToString(jHipsterProperties.getMail().getFrom()); + assertThat(message.getContent()).isInstanceOf(String.class); + assertThat(message.getContent()).hasToString("testContent"); + assertThat(message.getDataHandler().getContentType()).isEqualTo("text/html;charset=UTF-8"); + } + + @Test + void testSendMultipartEmail() throws Exception { + mailService.sendEmail("john.doe@example.com", "testSubject", "testContent", true, false); + verify(javaMailSender).send(messageCaptor.capture()); + MimeMessage message = messageCaptor.getValue(); + MimeMultipart mp = (MimeMultipart) message.getContent(); + MimeBodyPart part = (MimeBodyPart) ((MimeMultipart) mp.getBodyPart(0).getContent()).getBodyPart(0); + ByteArrayOutputStream aos = new ByteArrayOutputStream(); + part.writeTo(aos); + assertThat(message.getSubject()).isEqualTo("testSubject"); + assertThat(message.getAllRecipients()[0]).hasToString("john.doe@example.com"); + assertThat(message.getFrom()[0]).hasToString(jHipsterProperties.getMail().getFrom()); + assertThat(message.getContent()).isInstanceOf(Multipart.class); + assertThat(aos).hasToString("\r\ntestContent"); + assertThat(part.getDataHandler().getContentType()).isEqualTo("text/plain; charset=UTF-8"); + } + + @Test + void testSendMultipartHtmlEmail() throws Exception { + mailService.sendEmail("john.doe@example.com", "testSubject", "testContent", true, true); + verify(javaMailSender).send(messageCaptor.capture()); + MimeMessage message = messageCaptor.getValue(); + MimeMultipart mp = (MimeMultipart) message.getContent(); + MimeBodyPart part = (MimeBodyPart) ((MimeMultipart) mp.getBodyPart(0).getContent()).getBodyPart(0); + ByteArrayOutputStream aos = new ByteArrayOutputStream(); + part.writeTo(aos); + assertThat(message.getSubject()).isEqualTo("testSubject"); + assertThat(message.getAllRecipients()[0]).hasToString("john.doe@example.com"); + assertThat(message.getFrom()[0]).hasToString(jHipsterProperties.getMail().getFrom()); + assertThat(message.getContent()).isInstanceOf(Multipart.class); + assertThat(aos).hasToString("\r\ntestContent"); + assertThat(part.getDataHandler().getContentType()).isEqualTo("text/html;charset=UTF-8"); + } + + @Test + void testSendEmailFromTemplate() throws Exception { + User user = new User(); + user.setLangKey(Constants.DEFAULT_LANGUAGE); + user.setLogin("john"); + user.setEmail("john.doe@example.com"); + mailService.sendEmailFromTemplate(user, "mail/testEmail", "email.test.title"); + verify(javaMailSender).send(messageCaptor.capture()); + MimeMessage message = messageCaptor.getValue(); + assertThat(message.getSubject()).isEqualTo("test title"); + assertThat(message.getAllRecipients()[0]).hasToString(user.getEmail()); + assertThat(message.getFrom()[0]).hasToString(jHipsterProperties.getMail().getFrom()); + assertThat(message.getContent().toString()).isEqualToNormalizingNewlines("test title, http://127.0.0.1:8080, john\n"); + assertThat(message.getDataHandler().getContentType()).isEqualTo("text/html;charset=UTF-8"); + } + + @Test + void testSendActivationEmail() throws Exception { + User user = new User(); + user.setLangKey(Constants.DEFAULT_LANGUAGE); + user.setLogin("john"); + user.setEmail("john.doe@example.com"); + mailService.sendActivationEmail(user); + verify(javaMailSender).send(messageCaptor.capture()); + MimeMessage message = messageCaptor.getValue(); + assertThat(message.getAllRecipients()[0]).hasToString(user.getEmail()); + assertThat(message.getFrom()[0]).hasToString(jHipsterProperties.getMail().getFrom()); + assertThat(message.getContent().toString()).isNotEmpty(); + assertThat(message.getDataHandler().getContentType()).isEqualTo("text/html;charset=UTF-8"); + } + + @Test + void testCreationEmail() throws Exception { + User user = new User(); + user.setLangKey(Constants.DEFAULT_LANGUAGE); + user.setLogin("john"); + user.setEmail("john.doe@example.com"); + mailService.sendCreationEmail(user); + verify(javaMailSender).send(messageCaptor.capture()); + MimeMessage message = messageCaptor.getValue(); + assertThat(message.getAllRecipients()[0]).hasToString(user.getEmail()); + assertThat(message.getFrom()[0]).hasToString(jHipsterProperties.getMail().getFrom()); + assertThat(message.getContent().toString()).isNotEmpty(); + assertThat(message.getDataHandler().getContentType()).isEqualTo("text/html;charset=UTF-8"); + } + + @Test + void testSendPasswordResetMail() throws Exception { + User user = new User(); + user.setLangKey(Constants.DEFAULT_LANGUAGE); + user.setLogin("john"); + user.setEmail("john.doe@example.com"); + mailService.sendPasswordResetMail(user); + verify(javaMailSender).send(messageCaptor.capture()); + MimeMessage message = messageCaptor.getValue(); + assertThat(message.getAllRecipients()[0]).hasToString(user.getEmail()); + assertThat(message.getFrom()[0]).hasToString(jHipsterProperties.getMail().getFrom()); + assertThat(message.getContent().toString()).isNotEmpty(); + assertThat(message.getDataHandler().getContentType()).isEqualTo("text/html;charset=UTF-8"); + } + + @Test + void testSendEmailWithException() { + doThrow(MailSendException.class).when(javaMailSender).send(any(MimeMessage.class)); + try { + mailService.sendEmail("john.doe@example.com", "testSubject", "testContent", false, false); + } catch (Exception e) { + fail("Exception shouldn't have been thrown"); + } + } + + @Test + void testSendLocalizedEmailForAllSupportedLanguages() throws Exception { + User user = new User(); + user.setLogin("john"); + user.setEmail("john.doe@example.com"); + for (String langKey : languages) { + user.setLangKey(langKey); + mailService.sendEmailFromTemplate(user, "mail/testEmail", "email.test.title"); + verify(javaMailSender, atLeastOnce()).send(messageCaptor.capture()); + MimeMessage message = messageCaptor.getValue(); + + String propertyFilePath = "i18n/messages_" + getJavaLocale(langKey) + ".properties"; + URL resource = this.getClass().getClassLoader().getResource(propertyFilePath); + File file = new File(new URI(resource.getFile()).getPath()); + Properties properties = new Properties(); + properties.load(new InputStreamReader(new FileInputStream(file), Charset.forName("UTF-8"))); + + String emailTitle = (String) properties.get("email.test.title"); + assertThat(message.getSubject()).isEqualTo(emailTitle); + assertThat(message.getContent().toString()) + .isEqualToNormalizingNewlines("" + emailTitle + ", http://127.0.0.1:8080, john\n"); + } + } + + /** + * Convert a lang key to the Java locale. + */ + private String getJavaLocale(String langKey) { + String javaLangKey = langKey; + Matcher matcher2 = PATTERN_LOCALE_2.matcher(langKey); + if (matcher2.matches()) { + javaLangKey = matcher2.group(1) + "_" + matcher2.group(2).toUpperCase(); + } + Matcher matcher3 = PATTERN_LOCALE_3.matcher(langKey); + if (matcher3.matches()) { + javaLangKey = matcher3.group(1) + "_" + matcher3.group(2) + "_" + matcher3.group(3).toUpperCase(); + } + return javaLangKey; + } +} diff --git a/src/test/java/org/jhipster/health/service/UserServiceIT.java b/src/test/java/org/jhipster/health/service/UserServiceIT.java new file mode 100644 index 00000000..b77480b2 --- /dev/null +++ b/src/test/java/org/jhipster/health/service/UserServiceIT.java @@ -0,0 +1,204 @@ +package org.jhipster.health.service; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.temporal.ChronoUnit; +import java.util.List; +import java.util.Optional; +import org.apache.commons.lang3.RandomStringUtils; +import org.jhipster.health.IntegrationTest; +import org.jhipster.health.config.Constants; +import org.jhipster.health.domain.User; +import org.jhipster.health.repository.UserRepository; +import org.jhipster.health.repository.search.UserSearchRepository; +import org.jhipster.health.service.dto.AdminUserDTO; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.boot.test.mock.mockito.SpyBean; +import org.springframework.data.auditing.AuditingHandler; +import org.springframework.data.auditing.DateTimeProvider; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.transaction.annotation.Transactional; +import tech.jhipster.security.RandomUtil; + +/** + * Integration tests for {@link UserService}. + */ +@IntegrationTest +@Transactional +class UserServiceIT { + + private static final String DEFAULT_LOGIN = "johndoe"; + + private static final String DEFAULT_EMAIL = "johndoe@localhost"; + + private static final String DEFAULT_FIRSTNAME = "john"; + + private static final String DEFAULT_LASTNAME = "doe"; + + private static final String DEFAULT_IMAGEURL = "http://placehold.it/50x50"; + + private static final String DEFAULT_LANGKEY = "dummy"; + + @Autowired + private UserRepository userRepository; + + @Autowired + private UserService userService; + + /** + * This repository is mocked in the org.jhipster.health.repository.search test package. + * + * @see org.jhipster.health.repository.search.UserSearchRepositoryMockConfiguration + */ + @SpyBean + private UserSearchRepository spiedUserSearchRepository; + + @Autowired + private AuditingHandler auditingHandler; + + @MockBean + private DateTimeProvider dateTimeProvider; + + private User user; + + @BeforeEach + public void init() { + user = new User(); + user.setLogin(DEFAULT_LOGIN); + user.setPassword(RandomStringUtils.randomAlphanumeric(60)); + user.setActivated(true); + user.setEmail(DEFAULT_EMAIL); + user.setFirstName(DEFAULT_FIRSTNAME); + user.setLastName(DEFAULT_LASTNAME); + user.setImageUrl(DEFAULT_IMAGEURL); + user.setLangKey(DEFAULT_LANGKEY); + + when(dateTimeProvider.getNow()).thenReturn(Optional.of(LocalDateTime.now())); + auditingHandler.setDateTimeProvider(dateTimeProvider); + } + + @Test + @Transactional + void assertThatUserMustExistToResetPassword() { + userRepository.saveAndFlush(user); + Optional maybeUser = userService.requestPasswordReset("invalid.login@localhost"); + assertThat(maybeUser).isNotPresent(); + + maybeUser = userService.requestPasswordReset(user.getEmail()); + assertThat(maybeUser).isPresent(); + assertThat(maybeUser.orElse(null).getEmail()).isEqualTo(user.getEmail()); + assertThat(maybeUser.orElse(null).getResetDate()).isNotNull(); + assertThat(maybeUser.orElse(null).getResetKey()).isNotNull(); + } + + @Test + @Transactional + void assertThatOnlyActivatedUserCanRequestPasswordReset() { + user.setActivated(false); + userRepository.saveAndFlush(user); + + Optional maybeUser = userService.requestPasswordReset(user.getLogin()); + assertThat(maybeUser).isNotPresent(); + userRepository.delete(user); + } + + @Test + @Transactional + void assertThatResetKeyMustNotBeOlderThan24Hours() { + Instant daysAgo = Instant.now().minus(25, ChronoUnit.HOURS); + String resetKey = RandomUtil.generateResetKey(); + user.setActivated(true); + user.setResetDate(daysAgo); + user.setResetKey(resetKey); + userRepository.saveAndFlush(user); + + Optional maybeUser = userService.completePasswordReset("johndoe2", user.getResetKey()); + assertThat(maybeUser).isNotPresent(); + userRepository.delete(user); + } + + @Test + @Transactional + void assertThatResetKeyMustBeValid() { + Instant daysAgo = Instant.now().minus(25, ChronoUnit.HOURS); + user.setActivated(true); + user.setResetDate(daysAgo); + user.setResetKey("1234"); + userRepository.saveAndFlush(user); + + Optional maybeUser = userService.completePasswordReset("johndoe2", user.getResetKey()); + assertThat(maybeUser).isNotPresent(); + userRepository.delete(user); + } + + @Test + @Transactional + void assertThatUserCanResetPassword() { + String oldPassword = user.getPassword(); + Instant daysAgo = Instant.now().minus(2, ChronoUnit.HOURS); + String resetKey = RandomUtil.generateResetKey(); + user.setActivated(true); + user.setResetDate(daysAgo); + user.setResetKey(resetKey); + userRepository.saveAndFlush(user); + + Optional maybeUser = userService.completePasswordReset("johndoe2", user.getResetKey()); + assertThat(maybeUser).isPresent(); + assertThat(maybeUser.orElse(null).getResetDate()).isNull(); + assertThat(maybeUser.orElse(null).getResetKey()).isNull(); + assertThat(maybeUser.orElse(null).getPassword()).isNotEqualTo(oldPassword); + + userRepository.delete(user); + } + + @Test + @Transactional + void assertThatNotActivatedUsersWithNotNullActivationKeyCreatedBefore3DaysAreDeleted() { + Instant now = Instant.now(); + when(dateTimeProvider.getNow()).thenReturn(Optional.of(now.minus(4, ChronoUnit.DAYS))); + user.setActivated(false); + user.setActivationKey(RandomStringUtils.random(20)); + User dbUser = userRepository.saveAndFlush(user); + dbUser.setCreatedDate(now.minus(4, ChronoUnit.DAYS)); + userRepository.saveAndFlush(user); + Instant threeDaysAgo = now.minus(3, ChronoUnit.DAYS); + List users = userRepository.findAllByActivatedIsFalseAndActivationKeyIsNotNullAndCreatedDateBefore(threeDaysAgo); + assertThat(users).isNotEmpty(); + userService.removeNotActivatedUsers(); + users = userRepository.findAllByActivatedIsFalseAndActivationKeyIsNotNullAndCreatedDateBefore(threeDaysAgo); + assertThat(users).isEmpty(); + + // Verify Elasticsearch mock + verify(spiedUserSearchRepository, times(1)).delete(user); + } + + @Test + @Transactional + void assertThatNotActivatedUsersWithNullActivationKeyCreatedBefore3DaysAreNotDeleted() { + Instant now = Instant.now(); + when(dateTimeProvider.getNow()).thenReturn(Optional.of(now.minus(4, ChronoUnit.DAYS))); + user.setActivated(false); + User dbUser = userRepository.saveAndFlush(user); + dbUser.setCreatedDate(now.minus(4, ChronoUnit.DAYS)); + userRepository.saveAndFlush(user); + Instant threeDaysAgo = now.minus(3, ChronoUnit.DAYS); + List users = userRepository.findAllByActivatedIsFalseAndActivationKeyIsNotNullAndCreatedDateBefore(threeDaysAgo); + assertThat(users).isEmpty(); + userService.removeNotActivatedUsers(); + Optional maybeDbUser = userRepository.findById(dbUser.getId()); + assertThat(maybeDbUser).contains(dbUser); + + // Verify Elasticsearch mock + verify(spiedUserSearchRepository, never()).delete(user); + } +} diff --git a/src/test/java/org/jhipster/health/service/mapper/UserMapperTest.java b/src/test/java/org/jhipster/health/service/mapper/UserMapperTest.java new file mode 100644 index 00000000..58f87bff --- /dev/null +++ b/src/test/java/org/jhipster/health/service/mapper/UserMapperTest.java @@ -0,0 +1,132 @@ +package org.jhipster.health.service.mapper; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.apache.commons.lang3.RandomStringUtils; +import org.jhipster.health.domain.User; +import org.jhipster.health.service.dto.AdminUserDTO; +import org.jhipster.health.service.dto.UserDTO; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * Unit tests for {@link UserMapper}. + */ +class UserMapperTest { + + private static final String DEFAULT_LOGIN = "johndoe"; + private static final Long DEFAULT_ID = 1L; + + private UserMapper userMapper; + private User user; + private AdminUserDTO userDto; + + @BeforeEach + public void init() { + userMapper = new UserMapper(); + user = new User(); + user.setLogin(DEFAULT_LOGIN); + user.setPassword(RandomStringUtils.randomAlphanumeric(60)); + user.setActivated(true); + user.setEmail("johndoe@localhost"); + user.setFirstName("john"); + user.setLastName("doe"); + user.setImageUrl("image_url"); + user.setLangKey("en"); + + userDto = new AdminUserDTO(user); + } + + @Test + void usersToUserDTOsShouldMapOnlyNonNullUsers() { + List users = new ArrayList<>(); + users.add(user); + users.add(null); + + List userDTOS = userMapper.usersToUserDTOs(users); + + assertThat(userDTOS).isNotEmpty().size().isEqualTo(1); + } + + @Test + void userDTOsToUsersShouldMapOnlyNonNullUsers() { + List usersDto = new ArrayList<>(); + usersDto.add(userDto); + usersDto.add(null); + + List users = userMapper.userDTOsToUsers(usersDto); + + assertThat(users).isNotEmpty().size().isEqualTo(1); + } + + @Test + void userDTOsToUsersWithAuthoritiesStringShouldMapToUsersWithAuthoritiesDomain() { + Set authoritiesAsString = new HashSet<>(); + authoritiesAsString.add("ADMIN"); + userDto.setAuthorities(authoritiesAsString); + + List usersDto = new ArrayList<>(); + usersDto.add(userDto); + + List users = userMapper.userDTOsToUsers(usersDto); + + assertThat(users).isNotEmpty().size().isEqualTo(1); + assertThat(users.get(0).getAuthorities()).isNotNull(); + assertThat(users.get(0).getAuthorities()).isNotEmpty(); + assertThat(users.get(0).getAuthorities().iterator().next().getName()).isEqualTo("ADMIN"); + } + + @Test + void userDTOsToUsersMapWithNullAuthoritiesStringShouldReturnUserWithEmptyAuthorities() { + userDto.setAuthorities(null); + + List usersDto = new ArrayList<>(); + usersDto.add(userDto); + + List users = userMapper.userDTOsToUsers(usersDto); + + assertThat(users).isNotEmpty().size().isEqualTo(1); + assertThat(users.get(0).getAuthorities()).isNotNull(); + assertThat(users.get(0).getAuthorities()).isEmpty(); + } + + @Test + void userDTOToUserMapWithAuthoritiesStringShouldReturnUserWithAuthorities() { + Set authoritiesAsString = new HashSet<>(); + authoritiesAsString.add("ADMIN"); + userDto.setAuthorities(authoritiesAsString); + + User user = userMapper.userDTOToUser(userDto); + + assertThat(user).isNotNull(); + assertThat(user.getAuthorities()).isNotNull(); + assertThat(user.getAuthorities()).isNotEmpty(); + assertThat(user.getAuthorities().iterator().next().getName()).isEqualTo("ADMIN"); + } + + @Test + void userDTOToUserMapWithNullAuthoritiesStringShouldReturnUserWithEmptyAuthorities() { + userDto.setAuthorities(null); + + User user = userMapper.userDTOToUser(userDto); + + assertThat(user).isNotNull(); + assertThat(user.getAuthorities()).isNotNull(); + assertThat(user.getAuthorities()).isEmpty(); + } + + @Test + void userDTOToUserMapWithNullUserShouldReturnNull() { + assertThat(userMapper.userDTOToUser(null)).isNull(); + } + + @Test + void testUserFromId() { + assertThat(userMapper.userFromId(DEFAULT_ID).getId()).isEqualTo(DEFAULT_ID); + assertThat(userMapper.userFromId(null)).isNull(); + } +} diff --git a/src/test/java/org/jhipster/health/web/rest/AccountResourceIT.java b/src/test/java/org/jhipster/health/web/rest/AccountResourceIT.java new file mode 100644 index 00000000..5fb4c34d --- /dev/null +++ b/src/test/java/org/jhipster/health/web/rest/AccountResourceIT.java @@ -0,0 +1,762 @@ +package org.jhipster.health.web.rest; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.jhipster.health.web.rest.AccountResourceIT.TEST_USER_LOGIN; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import java.time.Instant; +import java.util.*; +import org.apache.commons.lang3.RandomStringUtils; +import org.jhipster.health.IntegrationTest; +import org.jhipster.health.config.Constants; +import org.jhipster.health.domain.User; +import org.jhipster.health.repository.AuthorityRepository; +import org.jhipster.health.repository.UserRepository; +import org.jhipster.health.security.AuthoritiesConstants; +import org.jhipster.health.service.UserService; +import org.jhipster.health.service.dto.AdminUserDTO; +import org.jhipster.health.service.dto.PasswordChangeDTO; +import org.jhipster.health.service.dto.UserDTO; +import org.jhipster.health.web.rest.vm.KeyAndPasswordVM; +import org.jhipster.health.web.rest.vm.ManagedUserVM; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.http.MediaType; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.transaction.annotation.Transactional; + +/** + * Integration tests for the {@link AccountResource} REST controller. + */ +@AutoConfigureMockMvc +@WithMockUser(value = TEST_USER_LOGIN) +@IntegrationTest +class AccountResourceIT { + + static final String TEST_USER_LOGIN = "test"; + + @Autowired + private UserRepository userRepository; + + @Autowired + private AuthorityRepository authorityRepository; + + @Autowired + private UserService userService; + + @Autowired + private PasswordEncoder passwordEncoder; + + @Autowired + private MockMvc restAccountMockMvc; + + @Test + @WithUnauthenticatedMockUser + void testNonAuthenticatedUser() throws Exception { + restAccountMockMvc + .perform(get("/api/authenticate").accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().string("")); + } + + @Test + void testAuthenticatedUser() throws Exception { + restAccountMockMvc + .perform( + get("/api/authenticate") + .with(request -> { + request.setRemoteUser(TEST_USER_LOGIN); + return request; + }) + .accept(MediaType.APPLICATION_JSON) + ) + .andExpect(status().isOk()) + .andExpect(content().string(TEST_USER_LOGIN)); + } + + @Test + void testGetExistingAccount() throws Exception { + Set authorities = new HashSet<>(); + authorities.add(AuthoritiesConstants.ADMIN); + + AdminUserDTO user = new AdminUserDTO(); + user.setLogin(TEST_USER_LOGIN); + user.setFirstName("john"); + user.setLastName("doe"); + user.setEmail("john.doe@jhipster.com"); + user.setImageUrl("http://placehold.it/50x50"); + user.setLangKey("en"); + user.setAuthorities(authorities); + userService.createUser(user); + + restAccountMockMvc + .perform(get("/api/account").accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(jsonPath("$.login").value(TEST_USER_LOGIN)) + .andExpect(jsonPath("$.firstName").value("john")) + .andExpect(jsonPath("$.lastName").value("doe")) + .andExpect(jsonPath("$.email").value("john.doe@jhipster.com")) + .andExpect(jsonPath("$.imageUrl").value("http://placehold.it/50x50")) + .andExpect(jsonPath("$.langKey").value("en")) + .andExpect(jsonPath("$.authorities").value(AuthoritiesConstants.ADMIN)); + } + + @Test + void testGetUnknownAccount() throws Exception { + restAccountMockMvc + .perform(get("/api/account").accept(MediaType.APPLICATION_PROBLEM_JSON)) + .andExpect(status().isInternalServerError()); + } + + @Test + @Transactional + void testRegisterValid() throws Exception { + ManagedUserVM validUser = new ManagedUserVM(); + validUser.setLogin("test-register-valid"); + validUser.setPassword("password"); + validUser.setFirstName("Alice"); + validUser.setLastName("Test"); + validUser.setEmail("test-register-valid@example.com"); + validUser.setImageUrl("http://placehold.it/50x50"); + validUser.setLangKey(Constants.DEFAULT_LANGUAGE); + validUser.setAuthorities(Collections.singleton(AuthoritiesConstants.USER)); + assertThat(userRepository.findOneByLogin("test-register-valid")).isEmpty(); + + restAccountMockMvc + .perform(post("/api/register").contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(validUser))) + .andExpect(status().isCreated()); + + assertThat(userRepository.findOneByLogin("test-register-valid")).isPresent(); + } + + @Test + @Transactional + void testRegisterInvalidLogin() throws Exception { + ManagedUserVM invalidUser = new ManagedUserVM(); + invalidUser.setLogin("funky-log(n"); // <-- invalid + invalidUser.setPassword("password"); + invalidUser.setFirstName("Funky"); + invalidUser.setLastName("One"); + invalidUser.setEmail("funky@example.com"); + invalidUser.setActivated(true); + invalidUser.setImageUrl("http://placehold.it/50x50"); + invalidUser.setLangKey(Constants.DEFAULT_LANGUAGE); + invalidUser.setAuthorities(Collections.singleton(AuthoritiesConstants.USER)); + + restAccountMockMvc + .perform(post("/api/register").contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(invalidUser))) + .andExpect(status().isBadRequest()); + + Optional user = userRepository.findOneByEmailIgnoreCase("funky@example.com"); + assertThat(user).isEmpty(); + } + + @Test + @Transactional + void testRegisterInvalidEmail() throws Exception { + ManagedUserVM invalidUser = new ManagedUserVM(); + invalidUser.setLogin("bob"); + invalidUser.setPassword("password"); + invalidUser.setFirstName("Bob"); + invalidUser.setLastName("Green"); + invalidUser.setEmail("invalid"); // <-- invalid + invalidUser.setActivated(true); + invalidUser.setImageUrl("http://placehold.it/50x50"); + invalidUser.setLangKey(Constants.DEFAULT_LANGUAGE); + invalidUser.setAuthorities(Collections.singleton(AuthoritiesConstants.USER)); + + restAccountMockMvc + .perform(post("/api/register").contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(invalidUser))) + .andExpect(status().isBadRequest()); + + Optional user = userRepository.findOneByLogin("bob"); + assertThat(user).isEmpty(); + } + + @Test + @Transactional + void testRegisterInvalidPassword() throws Exception { + ManagedUserVM invalidUser = new ManagedUserVM(); + invalidUser.setLogin("bob"); + invalidUser.setPassword("123"); // password with only 3 digits + invalidUser.setFirstName("Bob"); + invalidUser.setLastName("Green"); + invalidUser.setEmail("bob@example.com"); + invalidUser.setActivated(true); + invalidUser.setImageUrl("http://placehold.it/50x50"); + invalidUser.setLangKey(Constants.DEFAULT_LANGUAGE); + invalidUser.setAuthorities(Collections.singleton(AuthoritiesConstants.USER)); + + restAccountMockMvc + .perform(post("/api/register").contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(invalidUser))) + .andExpect(status().isBadRequest()); + + Optional user = userRepository.findOneByLogin("bob"); + assertThat(user).isEmpty(); + } + + @Test + @Transactional + void testRegisterNullPassword() throws Exception { + ManagedUserVM invalidUser = new ManagedUserVM(); + invalidUser.setLogin("bob"); + invalidUser.setPassword(null); // invalid null password + invalidUser.setFirstName("Bob"); + invalidUser.setLastName("Green"); + invalidUser.setEmail("bob@example.com"); + invalidUser.setActivated(true); + invalidUser.setImageUrl("http://placehold.it/50x50"); + invalidUser.setLangKey(Constants.DEFAULT_LANGUAGE); + invalidUser.setAuthorities(Collections.singleton(AuthoritiesConstants.USER)); + + restAccountMockMvc + .perform(post("/api/register").contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(invalidUser))) + .andExpect(status().isBadRequest()); + + Optional user = userRepository.findOneByLogin("bob"); + assertThat(user).isEmpty(); + } + + @Test + @Transactional + void testRegisterDuplicateLogin() throws Exception { + // First registration + ManagedUserVM firstUser = new ManagedUserVM(); + firstUser.setLogin("alice"); + firstUser.setPassword("password"); + firstUser.setFirstName("Alice"); + firstUser.setLastName("Something"); + firstUser.setEmail("alice@example.com"); + firstUser.setImageUrl("http://placehold.it/50x50"); + firstUser.setLangKey(Constants.DEFAULT_LANGUAGE); + firstUser.setAuthorities(Collections.singleton(AuthoritiesConstants.USER)); + + // Duplicate login, different email + ManagedUserVM secondUser = new ManagedUserVM(); + secondUser.setLogin(firstUser.getLogin()); + secondUser.setPassword(firstUser.getPassword()); + secondUser.setFirstName(firstUser.getFirstName()); + secondUser.setLastName(firstUser.getLastName()); + secondUser.setEmail("alice2@example.com"); + secondUser.setImageUrl(firstUser.getImageUrl()); + secondUser.setLangKey(firstUser.getLangKey()); + secondUser.setCreatedBy(firstUser.getCreatedBy()); + secondUser.setCreatedDate(firstUser.getCreatedDate()); + secondUser.setLastModifiedBy(firstUser.getLastModifiedBy()); + secondUser.setLastModifiedDate(firstUser.getLastModifiedDate()); + secondUser.setAuthorities(new HashSet<>(firstUser.getAuthorities())); + + // First user + restAccountMockMvc + .perform(post("/api/register").contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(firstUser))) + .andExpect(status().isCreated()); + + // Second (non activated) user + restAccountMockMvc + .perform(post("/api/register").contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(secondUser))) + .andExpect(status().isCreated()); + + Optional testUser = userRepository.findOneByEmailIgnoreCase("alice2@example.com"); + assertThat(testUser).isPresent(); + testUser.get().setActivated(true); + userRepository.save(testUser.get()); + + // Second (already activated) user + restAccountMockMvc + .perform(post("/api/register").contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(secondUser))) + .andExpect(status().is4xxClientError()); + } + + @Test + @Transactional + void testRegisterDuplicateEmail() throws Exception { + // First user + ManagedUserVM firstUser = new ManagedUserVM(); + firstUser.setLogin("test-register-duplicate-email"); + firstUser.setPassword("password"); + firstUser.setFirstName("Alice"); + firstUser.setLastName("Test"); + firstUser.setEmail("test-register-duplicate-email@example.com"); + firstUser.setImageUrl("http://placehold.it/50x50"); + firstUser.setLangKey(Constants.DEFAULT_LANGUAGE); + firstUser.setAuthorities(Collections.singleton(AuthoritiesConstants.USER)); + + // Register first user + restAccountMockMvc + .perform(post("/api/register").contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(firstUser))) + .andExpect(status().isCreated()); + + Optional testUser1 = userRepository.findOneByLogin("test-register-duplicate-email"); + assertThat(testUser1).isPresent(); + + // Duplicate email, different login + ManagedUserVM secondUser = new ManagedUserVM(); + secondUser.setLogin("test-register-duplicate-email-2"); + secondUser.setPassword(firstUser.getPassword()); + secondUser.setFirstName(firstUser.getFirstName()); + secondUser.setLastName(firstUser.getLastName()); + secondUser.setEmail(firstUser.getEmail()); + secondUser.setImageUrl(firstUser.getImageUrl()); + secondUser.setLangKey(firstUser.getLangKey()); + secondUser.setAuthorities(new HashSet<>(firstUser.getAuthorities())); + + // Register second (non activated) user + restAccountMockMvc + .perform(post("/api/register").contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(secondUser))) + .andExpect(status().isCreated()); + + Optional testUser2 = userRepository.findOneByLogin("test-register-duplicate-email"); + assertThat(testUser2).isEmpty(); + + Optional testUser3 = userRepository.findOneByLogin("test-register-duplicate-email-2"); + assertThat(testUser3).isPresent(); + + // Duplicate email - with uppercase email address + ManagedUserVM userWithUpperCaseEmail = new ManagedUserVM(); + userWithUpperCaseEmail.setId(firstUser.getId()); + userWithUpperCaseEmail.setLogin("test-register-duplicate-email-3"); + userWithUpperCaseEmail.setPassword(firstUser.getPassword()); + userWithUpperCaseEmail.setFirstName(firstUser.getFirstName()); + userWithUpperCaseEmail.setLastName(firstUser.getLastName()); + userWithUpperCaseEmail.setEmail("TEST-register-duplicate-email@example.com"); + userWithUpperCaseEmail.setImageUrl(firstUser.getImageUrl()); + userWithUpperCaseEmail.setLangKey(firstUser.getLangKey()); + userWithUpperCaseEmail.setAuthorities(new HashSet<>(firstUser.getAuthorities())); + + // Register third (not activated) user + restAccountMockMvc + .perform( + post("/api/register") + .contentType(MediaType.APPLICATION_JSON) + .content(TestUtil.convertObjectToJsonBytes(userWithUpperCaseEmail)) + ) + .andExpect(status().isCreated()); + + Optional testUser4 = userRepository.findOneByLogin("test-register-duplicate-email-3"); + assertThat(testUser4).isPresent(); + assertThat(testUser4.get().getEmail()).isEqualTo("test-register-duplicate-email@example.com"); + + testUser4.get().setActivated(true); + userService.updateUser((new AdminUserDTO(testUser4.get()))); + + // Register 4th (already activated) user + restAccountMockMvc + .perform(post("/api/register").contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(secondUser))) + .andExpect(status().is4xxClientError()); + } + + @Test + @Transactional + void testRegisterAdminIsIgnored() throws Exception { + ManagedUserVM validUser = new ManagedUserVM(); + validUser.setLogin("badguy"); + validUser.setPassword("password"); + validUser.setFirstName("Bad"); + validUser.setLastName("Guy"); + validUser.setEmail("badguy@example.com"); + validUser.setActivated(true); + validUser.setImageUrl("http://placehold.it/50x50"); + validUser.setLangKey(Constants.DEFAULT_LANGUAGE); + validUser.setAuthorities(Collections.singleton(AuthoritiesConstants.ADMIN)); + + restAccountMockMvc + .perform(post("/api/register").contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(validUser))) + .andExpect(status().isCreated()); + + Optional userDup = userRepository.findOneWithAuthoritiesByLogin("badguy"); + assertThat(userDup).isPresent(); + assertThat(userDup.get().getAuthorities()) + .hasSize(1) + .containsExactly(authorityRepository.findById(AuthoritiesConstants.USER).get()); + } + + @Test + @Transactional + void testActivateAccount() throws Exception { + final String activationKey = "some activation key"; + User user = new User(); + user.setLogin("activate-account"); + user.setEmail("activate-account@example.com"); + user.setPassword(RandomStringUtils.randomAlphanumeric(60)); + user.setActivated(false); + user.setActivationKey(activationKey); + + userRepository.saveAndFlush(user); + + restAccountMockMvc.perform(get("/api/activate?key={activationKey}", activationKey)).andExpect(status().isOk()); + + user = userRepository.findOneByLogin(user.getLogin()).orElse(null); + assertThat(user.isActivated()).isTrue(); + } + + @Test + @Transactional + void testActivateAccountWithWrongKey() throws Exception { + restAccountMockMvc.perform(get("/api/activate?key=wrongActivationKey")).andExpect(status().isInternalServerError()); + } + + @Test + @Transactional + @WithMockUser("save-account") + void testSaveAccount() throws Exception { + User user = new User(); + user.setLogin("save-account"); + user.setEmail("save-account@example.com"); + user.setPassword(RandomStringUtils.randomAlphanumeric(60)); + user.setActivated(true); + userRepository.saveAndFlush(user); + + AdminUserDTO userDTO = new AdminUserDTO(); + userDTO.setLogin("not-used"); + userDTO.setFirstName("firstname"); + userDTO.setLastName("lastname"); + userDTO.setEmail("save-account@example.com"); + userDTO.setActivated(false); + userDTO.setImageUrl("http://placehold.it/50x50"); + userDTO.setLangKey(Constants.DEFAULT_LANGUAGE); + userDTO.setAuthorities(Collections.singleton(AuthoritiesConstants.ADMIN)); + + restAccountMockMvc + .perform(post("/api/account").contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(userDTO))) + .andExpect(status().isOk()); + + User updatedUser = userRepository.findOneWithAuthoritiesByLogin(user.getLogin()).orElse(null); + assertThat(updatedUser.getFirstName()).isEqualTo(userDTO.getFirstName()); + assertThat(updatedUser.getLastName()).isEqualTo(userDTO.getLastName()); + assertThat(updatedUser.getEmail()).isEqualTo(userDTO.getEmail()); + assertThat(updatedUser.getLangKey()).isEqualTo(userDTO.getLangKey()); + assertThat(updatedUser.getPassword()).isEqualTo(user.getPassword()); + assertThat(updatedUser.getImageUrl()).isEqualTo(userDTO.getImageUrl()); + assertThat(updatedUser.isActivated()).isTrue(); + assertThat(updatedUser.getAuthorities()).isEmpty(); + } + + @Test + @Transactional + @WithMockUser("save-invalid-email") + void testSaveInvalidEmail() throws Exception { + User user = new User(); + user.setLogin("save-invalid-email"); + user.setEmail("save-invalid-email@example.com"); + user.setPassword(RandomStringUtils.randomAlphanumeric(60)); + user.setActivated(true); + + userRepository.saveAndFlush(user); + + AdminUserDTO userDTO = new AdminUserDTO(); + userDTO.setLogin("not-used"); + userDTO.setFirstName("firstname"); + userDTO.setLastName("lastname"); + userDTO.setEmail("invalid email"); + userDTO.setActivated(false); + userDTO.setImageUrl("http://placehold.it/50x50"); + userDTO.setLangKey(Constants.DEFAULT_LANGUAGE); + userDTO.setAuthorities(Collections.singleton(AuthoritiesConstants.ADMIN)); + + restAccountMockMvc + .perform(post("/api/account").contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(userDTO))) + .andExpect(status().isBadRequest()); + + assertThat(userRepository.findOneByEmailIgnoreCase("invalid email")).isNotPresent(); + } + + @Test + @Transactional + @WithMockUser("save-existing-email") + void testSaveExistingEmail() throws Exception { + User user = new User(); + user.setLogin("save-existing-email"); + user.setEmail("save-existing-email@example.com"); + user.setPassword(RandomStringUtils.randomAlphanumeric(60)); + user.setActivated(true); + userRepository.saveAndFlush(user); + + User anotherUser = new User(); + anotherUser.setLogin("save-existing-email2"); + anotherUser.setEmail("save-existing-email2@example.com"); + anotherUser.setPassword(RandomStringUtils.randomAlphanumeric(60)); + anotherUser.setActivated(true); + + userRepository.saveAndFlush(anotherUser); + + AdminUserDTO userDTO = new AdminUserDTO(); + userDTO.setLogin("not-used"); + userDTO.setFirstName("firstname"); + userDTO.setLastName("lastname"); + userDTO.setEmail("save-existing-email2@example.com"); + userDTO.setActivated(false); + userDTO.setImageUrl("http://placehold.it/50x50"); + userDTO.setLangKey(Constants.DEFAULT_LANGUAGE); + userDTO.setAuthorities(Collections.singleton(AuthoritiesConstants.ADMIN)); + + restAccountMockMvc + .perform(post("/api/account").contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(userDTO))) + .andExpect(status().isBadRequest()); + + User updatedUser = userRepository.findOneByLogin("save-existing-email").orElse(null); + assertThat(updatedUser.getEmail()).isEqualTo("save-existing-email@example.com"); + } + + @Test + @Transactional + @WithMockUser("save-existing-email-and-login") + void testSaveExistingEmailAndLogin() throws Exception { + User user = new User(); + user.setLogin("save-existing-email-and-login"); + user.setEmail("save-existing-email-and-login@example.com"); + user.setPassword(RandomStringUtils.randomAlphanumeric(60)); + user.setActivated(true); + userRepository.saveAndFlush(user); + + AdminUserDTO userDTO = new AdminUserDTO(); + userDTO.setLogin("not-used"); + userDTO.setFirstName("firstname"); + userDTO.setLastName("lastname"); + userDTO.setEmail("save-existing-email-and-login@example.com"); + userDTO.setActivated(false); + userDTO.setImageUrl("http://placehold.it/50x50"); + userDTO.setLangKey(Constants.DEFAULT_LANGUAGE); + userDTO.setAuthorities(Collections.singleton(AuthoritiesConstants.ADMIN)); + + restAccountMockMvc + .perform(post("/api/account").contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(userDTO))) + .andExpect(status().isOk()); + + User updatedUser = userRepository.findOneByLogin("save-existing-email-and-login").orElse(null); + assertThat(updatedUser.getEmail()).isEqualTo("save-existing-email-and-login@example.com"); + } + + @Test + @Transactional + @WithMockUser("change-password-wrong-existing-password") + void testChangePasswordWrongExistingPassword() throws Exception { + User user = new User(); + String currentPassword = RandomStringUtils.randomAlphanumeric(60); + user.setPassword(passwordEncoder.encode(currentPassword)); + user.setLogin("change-password-wrong-existing-password"); + user.setEmail("change-password-wrong-existing-password@example.com"); + userRepository.saveAndFlush(user); + + restAccountMockMvc + .perform( + post("/api/account/change-password") + .contentType(MediaType.APPLICATION_JSON) + .content(TestUtil.convertObjectToJsonBytes(new PasswordChangeDTO("1" + currentPassword, "new password"))) + ) + .andExpect(status().isBadRequest()); + + User updatedUser = userRepository.findOneByLogin("change-password-wrong-existing-password").orElse(null); + assertThat(passwordEncoder.matches("new password", updatedUser.getPassword())).isFalse(); + assertThat(passwordEncoder.matches(currentPassword, updatedUser.getPassword())).isTrue(); + } + + @Test + @Transactional + @WithMockUser("change-password") + void testChangePassword() throws Exception { + User user = new User(); + String currentPassword = RandomStringUtils.randomAlphanumeric(60); + user.setPassword(passwordEncoder.encode(currentPassword)); + user.setLogin("change-password"); + user.setEmail("change-password@example.com"); + userRepository.saveAndFlush(user); + + restAccountMockMvc + .perform( + post("/api/account/change-password") + .contentType(MediaType.APPLICATION_JSON) + .content(TestUtil.convertObjectToJsonBytes(new PasswordChangeDTO(currentPassword, "new password"))) + ) + .andExpect(status().isOk()); + + User updatedUser = userRepository.findOneByLogin("change-password").orElse(null); + assertThat(passwordEncoder.matches("new password", updatedUser.getPassword())).isTrue(); + } + + @Test + @Transactional + @WithMockUser("change-password-too-small") + void testChangePasswordTooSmall() throws Exception { + User user = new User(); + String currentPassword = RandomStringUtils.randomAlphanumeric(60); + user.setPassword(passwordEncoder.encode(currentPassword)); + user.setLogin("change-password-too-small"); + user.setEmail("change-password-too-small@example.com"); + userRepository.saveAndFlush(user); + + String newPassword = RandomStringUtils.random(ManagedUserVM.PASSWORD_MIN_LENGTH - 1); + + restAccountMockMvc + .perform( + post("/api/account/change-password") + .contentType(MediaType.APPLICATION_JSON) + .content(TestUtil.convertObjectToJsonBytes(new PasswordChangeDTO(currentPassword, newPassword))) + ) + .andExpect(status().isBadRequest()); + + User updatedUser = userRepository.findOneByLogin("change-password-too-small").orElse(null); + assertThat(updatedUser.getPassword()).isEqualTo(user.getPassword()); + } + + @Test + @Transactional + @WithMockUser("change-password-too-long") + void testChangePasswordTooLong() throws Exception { + User user = new User(); + String currentPassword = RandomStringUtils.randomAlphanumeric(60); + user.setPassword(passwordEncoder.encode(currentPassword)); + user.setLogin("change-password-too-long"); + user.setEmail("change-password-too-long@example.com"); + userRepository.saveAndFlush(user); + + String newPassword = RandomStringUtils.random(ManagedUserVM.PASSWORD_MAX_LENGTH + 1); + + restAccountMockMvc + .perform( + post("/api/account/change-password") + .contentType(MediaType.APPLICATION_JSON) + .content(TestUtil.convertObjectToJsonBytes(new PasswordChangeDTO(currentPassword, newPassword))) + ) + .andExpect(status().isBadRequest()); + + User updatedUser = userRepository.findOneByLogin("change-password-too-long").orElse(null); + assertThat(updatedUser.getPassword()).isEqualTo(user.getPassword()); + } + + @Test + @Transactional + @WithMockUser("change-password-empty") + void testChangePasswordEmpty() throws Exception { + User user = new User(); + String currentPassword = RandomStringUtils.randomAlphanumeric(60); + user.setPassword(passwordEncoder.encode(currentPassword)); + user.setLogin("change-password-empty"); + user.setEmail("change-password-empty@example.com"); + userRepository.saveAndFlush(user); + + restAccountMockMvc + .perform( + post("/api/account/change-password") + .contentType(MediaType.APPLICATION_JSON) + .content(TestUtil.convertObjectToJsonBytes(new PasswordChangeDTO(currentPassword, ""))) + ) + .andExpect(status().isBadRequest()); + + User updatedUser = userRepository.findOneByLogin("change-password-empty").orElse(null); + assertThat(updatedUser.getPassword()).isEqualTo(user.getPassword()); + } + + @Test + @Transactional + void testRequestPasswordReset() throws Exception { + User user = new User(); + user.setPassword(RandomStringUtils.randomAlphanumeric(60)); + user.setActivated(true); + user.setLogin("password-reset"); + user.setEmail("password-reset@example.com"); + user.setLangKey("en"); + userRepository.saveAndFlush(user); + + restAccountMockMvc + .perform(post("/api/account/reset-password/init").content("password-reset@example.com")) + .andExpect(status().isOk()); + } + + @Test + @Transactional + void testRequestPasswordResetUpperCaseEmail() throws Exception { + User user = new User(); + user.setPassword(RandomStringUtils.randomAlphanumeric(60)); + user.setActivated(true); + user.setLogin("password-reset-upper-case"); + user.setEmail("password-reset-upper-case@example.com"); + user.setLangKey("en"); + userRepository.saveAndFlush(user); + + restAccountMockMvc + .perform(post("/api/account/reset-password/init").content("password-reset-upper-case@EXAMPLE.COM")) + .andExpect(status().isOk()); + } + + @Test + void testRequestPasswordResetWrongEmail() throws Exception { + restAccountMockMvc + .perform(post("/api/account/reset-password/init").content("password-reset-wrong-email@example.com")) + .andExpect(status().isOk()); + } + + @Test + @Transactional + void testFinishPasswordReset() throws Exception { + User user = new User(); + user.setPassword(RandomStringUtils.randomAlphanumeric(60)); + user.setLogin("finish-password-reset"); + user.setEmail("finish-password-reset@example.com"); + user.setResetDate(Instant.now().plusSeconds(60)); + user.setResetKey("reset key"); + userRepository.saveAndFlush(user); + + KeyAndPasswordVM keyAndPassword = new KeyAndPasswordVM(); + keyAndPassword.setKey(user.getResetKey()); + keyAndPassword.setNewPassword("new password"); + + restAccountMockMvc + .perform( + post("/api/account/reset-password/finish") + .contentType(MediaType.APPLICATION_JSON) + .content(TestUtil.convertObjectToJsonBytes(keyAndPassword)) + ) + .andExpect(status().isOk()); + + User updatedUser = userRepository.findOneByLogin(user.getLogin()).orElse(null); + assertThat(passwordEncoder.matches(keyAndPassword.getNewPassword(), updatedUser.getPassword())).isTrue(); + } + + @Test + @Transactional + void testFinishPasswordResetTooSmall() throws Exception { + User user = new User(); + user.setPassword(RandomStringUtils.randomAlphanumeric(60)); + user.setLogin("finish-password-reset-too-small"); + user.setEmail("finish-password-reset-too-small@example.com"); + user.setResetDate(Instant.now().plusSeconds(60)); + user.setResetKey("reset key too small"); + userRepository.saveAndFlush(user); + + KeyAndPasswordVM keyAndPassword = new KeyAndPasswordVM(); + keyAndPassword.setKey(user.getResetKey()); + keyAndPassword.setNewPassword("foo"); + + restAccountMockMvc + .perform( + post("/api/account/reset-password/finish") + .contentType(MediaType.APPLICATION_JSON) + .content(TestUtil.convertObjectToJsonBytes(keyAndPassword)) + ) + .andExpect(status().isBadRequest()); + + User updatedUser = userRepository.findOneByLogin(user.getLogin()).orElse(null); + assertThat(passwordEncoder.matches(keyAndPassword.getNewPassword(), updatedUser.getPassword())).isFalse(); + } + + @Test + @Transactional + void testFinishPasswordResetWrongKey() throws Exception { + KeyAndPasswordVM keyAndPassword = new KeyAndPasswordVM(); + keyAndPassword.setKey("wrong reset key"); + keyAndPassword.setNewPassword("new password"); + + restAccountMockMvc + .perform( + post("/api/account/reset-password/finish") + .contentType(MediaType.APPLICATION_JSON) + .content(TestUtil.convertObjectToJsonBytes(keyAndPassword)) + ) + .andExpect(status().isInternalServerError()); + } +} diff --git a/src/test/java/org/jhipster/health/web/rest/BloodPressureResourceIT.java b/src/test/java/org/jhipster/health/web/rest/BloodPressureResourceIT.java new file mode 100644 index 00000000..96acf49d --- /dev/null +++ b/src/test/java/org/jhipster/health/web/rest/BloodPressureResourceIT.java @@ -0,0 +1,568 @@ +package org.jhipster.health.web.rest; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; +import static org.hamcrest.Matchers.hasItem; +import static org.jhipster.health.web.rest.TestUtil.sameInstant; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Random; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; +import javax.persistence.EntityManager; +import org.apache.commons.collections4.IterableUtils; +import org.assertj.core.util.IterableUtil; +import org.jhipster.health.IntegrationTest; +import org.jhipster.health.domain.BloodPressure; +import org.jhipster.health.repository.BloodPressureRepository; +import org.jhipster.health.repository.search.BloodPressureSearchRepository; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.http.MediaType; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.transaction.annotation.Transactional; + +/** + * Integration tests for the {@link BloodPressureResource} REST controller. + */ +@IntegrationTest +@ExtendWith(MockitoExtension.class) +@AutoConfigureMockMvc +@WithMockUser +class BloodPressureResourceIT { + + private static final ZonedDateTime DEFAULT_TIMESTAMP = ZonedDateTime.ofInstant(Instant.ofEpochMilli(0L), ZoneOffset.UTC); + private static final ZonedDateTime UPDATED_TIMESTAMP = ZonedDateTime.now(ZoneId.systemDefault()).withNano(0); + + private static final Integer DEFAULT_SYSTOLIC = 1; + private static final Integer UPDATED_SYSTOLIC = 2; + + private static final Integer DEFAULT_DIASTOLIC = 1; + private static final Integer UPDATED_DIASTOLIC = 2; + + private static final String ENTITY_API_URL = "/api/blood-pressures"; + private static final String ENTITY_API_URL_ID = ENTITY_API_URL + "/{id}"; + private static final String ENTITY_SEARCH_API_URL = "/api/_search/blood-pressures"; + + private static Random random = new Random(); + private static AtomicLong count = new AtomicLong(random.nextInt() + (2 * Integer.MAX_VALUE)); + + @Autowired + private BloodPressureRepository bloodPressureRepository; + + @Mock + private BloodPressureRepository bloodPressureRepositoryMock; + + @Autowired + private BloodPressureSearchRepository bloodPressureSearchRepository; + + @Autowired + private EntityManager em; + + @Autowired + private MockMvc restBloodPressureMockMvc; + + private BloodPressure bloodPressure; + + /** + * Create an entity for this test. + * + * This is a static method, as tests for other entities might also need it, + * if they test an entity which requires the current entity. + */ + public static BloodPressure createEntity(EntityManager em) { + BloodPressure bloodPressure = new BloodPressure() + .timestamp(DEFAULT_TIMESTAMP) + .systolic(DEFAULT_SYSTOLIC) + .diastolic(DEFAULT_DIASTOLIC); + return bloodPressure; + } + + /** + * Create an updated entity for this test. + * + * This is a static method, as tests for other entities might also need it, + * if they test an entity which requires the current entity. + */ + public static BloodPressure createUpdatedEntity(EntityManager em) { + BloodPressure bloodPressure = new BloodPressure() + .timestamp(UPDATED_TIMESTAMP) + .systolic(UPDATED_SYSTOLIC) + .diastolic(UPDATED_DIASTOLIC); + return bloodPressure; + } + + @AfterEach + public void cleanupElasticSearchRepository() { + bloodPressureSearchRepository.deleteAll(); + assertThat(bloodPressureSearchRepository.count()).isEqualTo(0); + } + + @BeforeEach + public void initTest() { + bloodPressure = createEntity(em); + } + + @Test + @Transactional + void createBloodPressure() throws Exception { + int databaseSizeBeforeCreate = bloodPressureRepository.findAll().size(); + int searchDatabaseSizeBefore = IterableUtil.sizeOf(bloodPressureSearchRepository.findAll()); + // Create the BloodPressure + restBloodPressureMockMvc + .perform(post(ENTITY_API_URL).contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(bloodPressure))) + .andExpect(status().isCreated()); + + // Validate the BloodPressure in the database + List bloodPressureList = bloodPressureRepository.findAll(); + assertThat(bloodPressureList).hasSize(databaseSizeBeforeCreate + 1); + await() + .atMost(5, TimeUnit.SECONDS) + .untilAsserted(() -> { + int searchDatabaseSizeAfter = IterableUtil.sizeOf(bloodPressureSearchRepository.findAll()); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore + 1); + }); + BloodPressure testBloodPressure = bloodPressureList.get(bloodPressureList.size() - 1); + assertThat(testBloodPressure.getTimestamp()).isEqualTo(DEFAULT_TIMESTAMP); + assertThat(testBloodPressure.getSystolic()).isEqualTo(DEFAULT_SYSTOLIC); + assertThat(testBloodPressure.getDiastolic()).isEqualTo(DEFAULT_DIASTOLIC); + } + + @Test + @Transactional + void createBloodPressureWithExistingId() throws Exception { + // Create the BloodPressure with an existing ID + bloodPressure.setId(1L); + + int databaseSizeBeforeCreate = bloodPressureRepository.findAll().size(); + int searchDatabaseSizeBefore = IterableUtil.sizeOf(bloodPressureSearchRepository.findAll()); + + // An entity with an existing ID cannot be created, so this API call must fail + restBloodPressureMockMvc + .perform(post(ENTITY_API_URL).contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(bloodPressure))) + .andExpect(status().isBadRequest()); + + // Validate the BloodPressure in the database + List bloodPressureList = bloodPressureRepository.findAll(); + assertThat(bloodPressureList).hasSize(databaseSizeBeforeCreate); + int searchDatabaseSizeAfter = IterableUtil.sizeOf(bloodPressureSearchRepository.findAll()); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + } + + @Test + @Transactional + void checkTimestampIsRequired() throws Exception { + int databaseSizeBeforeTest = bloodPressureRepository.findAll().size(); + int searchDatabaseSizeBefore = IterableUtil.sizeOf(bloodPressureSearchRepository.findAll()); + // set the field null + bloodPressure.setTimestamp(null); + + // Create the BloodPressure, which fails. + + restBloodPressureMockMvc + .perform(post(ENTITY_API_URL).contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(bloodPressure))) + .andExpect(status().isBadRequest()); + + List bloodPressureList = bloodPressureRepository.findAll(); + assertThat(bloodPressureList).hasSize(databaseSizeBeforeTest); + int searchDatabaseSizeAfter = IterableUtil.sizeOf(bloodPressureSearchRepository.findAll()); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + } + + @Test + @Transactional + void checkSystolicIsRequired() throws Exception { + int databaseSizeBeforeTest = bloodPressureRepository.findAll().size(); + int searchDatabaseSizeBefore = IterableUtil.sizeOf(bloodPressureSearchRepository.findAll()); + // set the field null + bloodPressure.setSystolic(null); + + // Create the BloodPressure, which fails. + + restBloodPressureMockMvc + .perform(post(ENTITY_API_URL).contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(bloodPressure))) + .andExpect(status().isBadRequest()); + + List bloodPressureList = bloodPressureRepository.findAll(); + assertThat(bloodPressureList).hasSize(databaseSizeBeforeTest); + int searchDatabaseSizeAfter = IterableUtil.sizeOf(bloodPressureSearchRepository.findAll()); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + } + + @Test + @Transactional + void checkDiastolicIsRequired() throws Exception { + int databaseSizeBeforeTest = bloodPressureRepository.findAll().size(); + int searchDatabaseSizeBefore = IterableUtil.sizeOf(bloodPressureSearchRepository.findAll()); + // set the field null + bloodPressure.setDiastolic(null); + + // Create the BloodPressure, which fails. + + restBloodPressureMockMvc + .perform(post(ENTITY_API_URL).contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(bloodPressure))) + .andExpect(status().isBadRequest()); + + List bloodPressureList = bloodPressureRepository.findAll(); + assertThat(bloodPressureList).hasSize(databaseSizeBeforeTest); + int searchDatabaseSizeAfter = IterableUtil.sizeOf(bloodPressureSearchRepository.findAll()); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + } + + @Test + @Transactional + void getAllBloodPressures() throws Exception { + // Initialize the database + bloodPressureRepository.saveAndFlush(bloodPressure); + + // Get all the bloodPressureList + restBloodPressureMockMvc + .perform(get(ENTITY_API_URL + "?sort=id,desc")) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(jsonPath("$.[*].id").value(hasItem(bloodPressure.getId().intValue()))) + .andExpect(jsonPath("$.[*].timestamp").value(hasItem(sameInstant(DEFAULT_TIMESTAMP)))) + .andExpect(jsonPath("$.[*].systolic").value(hasItem(DEFAULT_SYSTOLIC))) + .andExpect(jsonPath("$.[*].diastolic").value(hasItem(DEFAULT_DIASTOLIC))); + } + + @SuppressWarnings({ "unchecked" }) + void getAllBloodPressuresWithEagerRelationshipsIsEnabled() throws Exception { + when(bloodPressureRepositoryMock.findAllWithEagerRelationships(any())).thenReturn(new PageImpl(new ArrayList<>())); + + restBloodPressureMockMvc.perform(get(ENTITY_API_URL + "?eagerload=true")).andExpect(status().isOk()); + + verify(bloodPressureRepositoryMock, times(1)).findAllWithEagerRelationships(any()); + } + + @SuppressWarnings({ "unchecked" }) + void getAllBloodPressuresWithEagerRelationshipsIsNotEnabled() throws Exception { + when(bloodPressureRepositoryMock.findAllWithEagerRelationships(any())).thenReturn(new PageImpl(new ArrayList<>())); + + restBloodPressureMockMvc.perform(get(ENTITY_API_URL + "?eagerload=false")).andExpect(status().isOk()); + verify(bloodPressureRepositoryMock, times(1)).findAll(any(Pageable.class)); + } + + @Test + @Transactional + void getBloodPressure() throws Exception { + // Initialize the database + bloodPressureRepository.saveAndFlush(bloodPressure); + + // Get the bloodPressure + restBloodPressureMockMvc + .perform(get(ENTITY_API_URL_ID, bloodPressure.getId())) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(jsonPath("$.id").value(bloodPressure.getId().intValue())) + .andExpect(jsonPath("$.timestamp").value(sameInstant(DEFAULT_TIMESTAMP))) + .andExpect(jsonPath("$.systolic").value(DEFAULT_SYSTOLIC)) + .andExpect(jsonPath("$.diastolic").value(DEFAULT_DIASTOLIC)); + } + + @Test + @Transactional + void getNonExistingBloodPressure() throws Exception { + // Get the bloodPressure + restBloodPressureMockMvc.perform(get(ENTITY_API_URL_ID, Long.MAX_VALUE)).andExpect(status().isNotFound()); + } + + @Test + @Transactional + void putExistingBloodPressure() throws Exception { + // Initialize the database + bloodPressureRepository.saveAndFlush(bloodPressure); + + int databaseSizeBeforeUpdate = bloodPressureRepository.findAll().size(); + bloodPressureSearchRepository.save(bloodPressure); + int searchDatabaseSizeBefore = IterableUtil.sizeOf(bloodPressureSearchRepository.findAll()); + + // Update the bloodPressure + BloodPressure updatedBloodPressure = bloodPressureRepository.findById(bloodPressure.getId()).get(); + // Disconnect from session so that the updates on updatedBloodPressure are not directly saved in db + em.detach(updatedBloodPressure); + updatedBloodPressure.timestamp(UPDATED_TIMESTAMP).systolic(UPDATED_SYSTOLIC).diastolic(UPDATED_DIASTOLIC); + + restBloodPressureMockMvc + .perform( + put(ENTITY_API_URL_ID, updatedBloodPressure.getId()) + .contentType(MediaType.APPLICATION_JSON) + .content(TestUtil.convertObjectToJsonBytes(updatedBloodPressure)) + ) + .andExpect(status().isOk()); + + // Validate the BloodPressure in the database + List bloodPressureList = bloodPressureRepository.findAll(); + assertThat(bloodPressureList).hasSize(databaseSizeBeforeUpdate); + BloodPressure testBloodPressure = bloodPressureList.get(bloodPressureList.size() - 1); + assertThat(testBloodPressure.getTimestamp()).isEqualTo(UPDATED_TIMESTAMP); + assertThat(testBloodPressure.getSystolic()).isEqualTo(UPDATED_SYSTOLIC); + assertThat(testBloodPressure.getDiastolic()).isEqualTo(UPDATED_DIASTOLIC); + await() + .atMost(5, TimeUnit.SECONDS) + .untilAsserted(() -> { + int searchDatabaseSizeAfter = IterableUtil.sizeOf(bloodPressureSearchRepository.findAll()); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + List bloodPressureSearchList = IterableUtils.toList(bloodPressureSearchRepository.findAll()); + BloodPressure testBloodPressureSearch = bloodPressureSearchList.get(searchDatabaseSizeAfter - 1); + assertThat(testBloodPressureSearch.getTimestamp()).isEqualTo(UPDATED_TIMESTAMP); + assertThat(testBloodPressureSearch.getSystolic()).isEqualTo(UPDATED_SYSTOLIC); + assertThat(testBloodPressureSearch.getDiastolic()).isEqualTo(UPDATED_DIASTOLIC); + }); + } + + @Test + @Transactional + void putNonExistingBloodPressure() throws Exception { + int databaseSizeBeforeUpdate = bloodPressureRepository.findAll().size(); + int searchDatabaseSizeBefore = IterableUtil.sizeOf(bloodPressureSearchRepository.findAll()); + bloodPressure.setId(count.incrementAndGet()); + + // If the entity doesn't have an ID, it will throw BadRequestAlertException + restBloodPressureMockMvc + .perform( + put(ENTITY_API_URL_ID, bloodPressure.getId()) + .contentType(MediaType.APPLICATION_JSON) + .content(TestUtil.convertObjectToJsonBytes(bloodPressure)) + ) + .andExpect(status().isBadRequest()); + + // Validate the BloodPressure in the database + List bloodPressureList = bloodPressureRepository.findAll(); + assertThat(bloodPressureList).hasSize(databaseSizeBeforeUpdate); + int searchDatabaseSizeAfter = IterableUtil.sizeOf(bloodPressureSearchRepository.findAll()); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + } + + @Test + @Transactional + void putWithIdMismatchBloodPressure() throws Exception { + int databaseSizeBeforeUpdate = bloodPressureRepository.findAll().size(); + int searchDatabaseSizeBefore = IterableUtil.sizeOf(bloodPressureSearchRepository.findAll()); + bloodPressure.setId(count.incrementAndGet()); + + // If url ID doesn't match entity ID, it will throw BadRequestAlertException + restBloodPressureMockMvc + .perform( + put(ENTITY_API_URL_ID, count.incrementAndGet()) + .contentType(MediaType.APPLICATION_JSON) + .content(TestUtil.convertObjectToJsonBytes(bloodPressure)) + ) + .andExpect(status().isBadRequest()); + + // Validate the BloodPressure in the database + List bloodPressureList = bloodPressureRepository.findAll(); + assertThat(bloodPressureList).hasSize(databaseSizeBeforeUpdate); + int searchDatabaseSizeAfter = IterableUtil.sizeOf(bloodPressureSearchRepository.findAll()); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + } + + @Test + @Transactional + void putWithMissingIdPathParamBloodPressure() throws Exception { + int databaseSizeBeforeUpdate = bloodPressureRepository.findAll().size(); + int searchDatabaseSizeBefore = IterableUtil.sizeOf(bloodPressureSearchRepository.findAll()); + bloodPressure.setId(count.incrementAndGet()); + + // If url ID doesn't match entity ID, it will throw BadRequestAlertException + restBloodPressureMockMvc + .perform(put(ENTITY_API_URL).contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(bloodPressure))) + .andExpect(status().isMethodNotAllowed()); + + // Validate the BloodPressure in the database + List bloodPressureList = bloodPressureRepository.findAll(); + assertThat(bloodPressureList).hasSize(databaseSizeBeforeUpdate); + int searchDatabaseSizeAfter = IterableUtil.sizeOf(bloodPressureSearchRepository.findAll()); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + } + + @Test + @Transactional + void partialUpdateBloodPressureWithPatch() throws Exception { + // Initialize the database + bloodPressureRepository.saveAndFlush(bloodPressure); + + int databaseSizeBeforeUpdate = bloodPressureRepository.findAll().size(); + + // Update the bloodPressure using partial update + BloodPressure partialUpdatedBloodPressure = new BloodPressure(); + partialUpdatedBloodPressure.setId(bloodPressure.getId()); + + partialUpdatedBloodPressure.diastolic(UPDATED_DIASTOLIC); + + restBloodPressureMockMvc + .perform( + patch(ENTITY_API_URL_ID, partialUpdatedBloodPressure.getId()) + .contentType("application/merge-patch+json") + .content(TestUtil.convertObjectToJsonBytes(partialUpdatedBloodPressure)) + ) + .andExpect(status().isOk()); + + // Validate the BloodPressure in the database + List bloodPressureList = bloodPressureRepository.findAll(); + assertThat(bloodPressureList).hasSize(databaseSizeBeforeUpdate); + BloodPressure testBloodPressure = bloodPressureList.get(bloodPressureList.size() - 1); + assertThat(testBloodPressure.getTimestamp()).isEqualTo(DEFAULT_TIMESTAMP); + assertThat(testBloodPressure.getSystolic()).isEqualTo(DEFAULT_SYSTOLIC); + assertThat(testBloodPressure.getDiastolic()).isEqualTo(UPDATED_DIASTOLIC); + } + + @Test + @Transactional + void fullUpdateBloodPressureWithPatch() throws Exception { + // Initialize the database + bloodPressureRepository.saveAndFlush(bloodPressure); + + int databaseSizeBeforeUpdate = bloodPressureRepository.findAll().size(); + + // Update the bloodPressure using partial update + BloodPressure partialUpdatedBloodPressure = new BloodPressure(); + partialUpdatedBloodPressure.setId(bloodPressure.getId()); + + partialUpdatedBloodPressure.timestamp(UPDATED_TIMESTAMP).systolic(UPDATED_SYSTOLIC).diastolic(UPDATED_DIASTOLIC); + + restBloodPressureMockMvc + .perform( + patch(ENTITY_API_URL_ID, partialUpdatedBloodPressure.getId()) + .contentType("application/merge-patch+json") + .content(TestUtil.convertObjectToJsonBytes(partialUpdatedBloodPressure)) + ) + .andExpect(status().isOk()); + + // Validate the BloodPressure in the database + List bloodPressureList = bloodPressureRepository.findAll(); + assertThat(bloodPressureList).hasSize(databaseSizeBeforeUpdate); + BloodPressure testBloodPressure = bloodPressureList.get(bloodPressureList.size() - 1); + assertThat(testBloodPressure.getTimestamp()).isEqualTo(UPDATED_TIMESTAMP); + assertThat(testBloodPressure.getSystolic()).isEqualTo(UPDATED_SYSTOLIC); + assertThat(testBloodPressure.getDiastolic()).isEqualTo(UPDATED_DIASTOLIC); + } + + @Test + @Transactional + void patchNonExistingBloodPressure() throws Exception { + int databaseSizeBeforeUpdate = bloodPressureRepository.findAll().size(); + int searchDatabaseSizeBefore = IterableUtil.sizeOf(bloodPressureSearchRepository.findAll()); + bloodPressure.setId(count.incrementAndGet()); + + // If the entity doesn't have an ID, it will throw BadRequestAlertException + restBloodPressureMockMvc + .perform( + patch(ENTITY_API_URL_ID, bloodPressure.getId()) + .contentType("application/merge-patch+json") + .content(TestUtil.convertObjectToJsonBytes(bloodPressure)) + ) + .andExpect(status().isBadRequest()); + + // Validate the BloodPressure in the database + List bloodPressureList = bloodPressureRepository.findAll(); + assertThat(bloodPressureList).hasSize(databaseSizeBeforeUpdate); + int searchDatabaseSizeAfter = IterableUtil.sizeOf(bloodPressureSearchRepository.findAll()); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + } + + @Test + @Transactional + void patchWithIdMismatchBloodPressure() throws Exception { + int databaseSizeBeforeUpdate = bloodPressureRepository.findAll().size(); + int searchDatabaseSizeBefore = IterableUtil.sizeOf(bloodPressureSearchRepository.findAll()); + bloodPressure.setId(count.incrementAndGet()); + + // If url ID doesn't match entity ID, it will throw BadRequestAlertException + restBloodPressureMockMvc + .perform( + patch(ENTITY_API_URL_ID, count.incrementAndGet()) + .contentType("application/merge-patch+json") + .content(TestUtil.convertObjectToJsonBytes(bloodPressure)) + ) + .andExpect(status().isBadRequest()); + + // Validate the BloodPressure in the database + List bloodPressureList = bloodPressureRepository.findAll(); + assertThat(bloodPressureList).hasSize(databaseSizeBeforeUpdate); + int searchDatabaseSizeAfter = IterableUtil.sizeOf(bloodPressureSearchRepository.findAll()); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + } + + @Test + @Transactional + void patchWithMissingIdPathParamBloodPressure() throws Exception { + int databaseSizeBeforeUpdate = bloodPressureRepository.findAll().size(); + int searchDatabaseSizeBefore = IterableUtil.sizeOf(bloodPressureSearchRepository.findAll()); + bloodPressure.setId(count.incrementAndGet()); + + // If url ID doesn't match entity ID, it will throw BadRequestAlertException + restBloodPressureMockMvc + .perform( + patch(ENTITY_API_URL).contentType("application/merge-patch+json").content(TestUtil.convertObjectToJsonBytes(bloodPressure)) + ) + .andExpect(status().isMethodNotAllowed()); + + // Validate the BloodPressure in the database + List bloodPressureList = bloodPressureRepository.findAll(); + assertThat(bloodPressureList).hasSize(databaseSizeBeforeUpdate); + int searchDatabaseSizeAfter = IterableUtil.sizeOf(bloodPressureSearchRepository.findAll()); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + } + + @Test + @Transactional + void deleteBloodPressure() throws Exception { + // Initialize the database + bloodPressureRepository.saveAndFlush(bloodPressure); + bloodPressureRepository.save(bloodPressure); + bloodPressureSearchRepository.save(bloodPressure); + + int databaseSizeBeforeDelete = bloodPressureRepository.findAll().size(); + int searchDatabaseSizeBefore = IterableUtil.sizeOf(bloodPressureSearchRepository.findAll()); + assertThat(searchDatabaseSizeBefore).isEqualTo(databaseSizeBeforeDelete); + + // Delete the bloodPressure + restBloodPressureMockMvc + .perform(delete(ENTITY_API_URL_ID, bloodPressure.getId()).accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isNoContent()); + + // Validate the database contains one less item + List bloodPressureList = bloodPressureRepository.findAll(); + assertThat(bloodPressureList).hasSize(databaseSizeBeforeDelete - 1); + int searchDatabaseSizeAfter = IterableUtil.sizeOf(bloodPressureSearchRepository.findAll()); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore - 1); + } + + @Test + @Transactional + void searchBloodPressure() throws Exception { + // Initialize the database + bloodPressure = bloodPressureRepository.saveAndFlush(bloodPressure); + bloodPressureSearchRepository.save(bloodPressure); + + // Search the bloodPressure + restBloodPressureMockMvc + .perform(get(ENTITY_SEARCH_API_URL + "?query=id:" + bloodPressure.getId())) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(jsonPath("$.[*].id").value(hasItem(bloodPressure.getId().intValue()))) + .andExpect(jsonPath("$.[*].timestamp").value(hasItem(sameInstant(DEFAULT_TIMESTAMP)))) + .andExpect(jsonPath("$.[*].systolic").value(hasItem(DEFAULT_SYSTOLIC))) + .andExpect(jsonPath("$.[*].diastolic").value(hasItem(DEFAULT_DIASTOLIC))); + } +} diff --git a/src/test/java/org/jhipster/health/web/rest/ClientForwardControllerTest.java b/src/test/java/org/jhipster/health/web/rest/ClientForwardControllerTest.java new file mode 100644 index 00000000..030cdec2 --- /dev/null +++ b/src/test/java/org/jhipster/health/web/rest/ClientForwardControllerTest.java @@ -0,0 +1,68 @@ +package org.jhipster.health.web.rest; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.forwardedUrl; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.http.MediaType; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * Unit tests for the {@link ClientForwardController} REST controller. + */ +class ClientForwardControllerTest { + + private MockMvc restMockMvc; + + @BeforeEach + public void setup() { + ClientForwardController clientForwardController = new ClientForwardController(); + this.restMockMvc = MockMvcBuilders.standaloneSetup(clientForwardController, new TestController()).build(); + } + + @Test + void getBackendEndpoint() throws Exception { + restMockMvc + .perform(get("/test")) + .andExpect(status().isOk()) + .andExpect(content().contentTypeCompatibleWith(MediaType.TEXT_PLAIN_VALUE)) + .andExpect(content().string("test")); + } + + @Test + void getClientEndpoint() throws Exception { + ResultActions perform = restMockMvc.perform(get("/non-existant-mapping")); + perform.andExpect(status().isOk()).andExpect(forwardedUrl("/")); + } + + @Test + void getNestedClientEndpoint() throws Exception { + restMockMvc.perform(get("/admin/user-management")).andExpect(status().isOk()).andExpect(forwardedUrl("/")); + } + + @Test + void getUnmappedDottedEndpoint() throws Exception { + restMockMvc.perform(get("/foo.js")).andExpect(status().isNotFound()); + } + + @Test + void getUnmappedNestedDottedEndpoint() throws Exception { + restMockMvc.perform(get("/foo/bar.js")).andExpect(status().isNotFound()); + } + + @RestController + public static class TestController { + + @RequestMapping(value = "/test") + public String test() { + return "test"; + } + } +} diff --git a/src/test/java/org/jhipster/health/web/rest/PointsResourceIT.java b/src/test/java/org/jhipster/health/web/rest/PointsResourceIT.java new file mode 100644 index 00000000..45ba99fa --- /dev/null +++ b/src/test/java/org/jhipster/health/web/rest/PointsResourceIT.java @@ -0,0 +1,554 @@ +package org.jhipster.health.web.rest; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; +import static org.hamcrest.Matchers.hasItem; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Random; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; +import javax.persistence.EntityManager; +import org.apache.commons.collections4.IterableUtils; +import org.assertj.core.util.IterableUtil; +import org.jhipster.health.IntegrationTest; +import org.jhipster.health.domain.Points; +import org.jhipster.health.repository.PointsRepository; +import org.jhipster.health.repository.search.PointsSearchRepository; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.http.MediaType; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.transaction.annotation.Transactional; + +/** + * Integration tests for the {@link PointsResource} REST controller. + */ +@IntegrationTest +@ExtendWith(MockitoExtension.class) +@AutoConfigureMockMvc +@WithMockUser +class PointsResourceIT { + + private static final LocalDate DEFAULT_DATE = LocalDate.ofEpochDay(0L); + private static final LocalDate UPDATED_DATE = LocalDate.now(ZoneId.systemDefault()); + + private static final Integer DEFAULT_EXERCISE = 1; + private static final Integer UPDATED_EXERCISE = 2; + + private static final Integer DEFAULT_MEALS = 1; + private static final Integer UPDATED_MEALS = 2; + + private static final Integer DEFAULT_ALCOHOL = 1; + private static final Integer UPDATED_ALCOHOL = 2; + + private static final String DEFAULT_NOTES = "AAAAAAAAAA"; + private static final String UPDATED_NOTES = "BBBBBBBBBB"; + + private static final String ENTITY_API_URL = "/api/points"; + private static final String ENTITY_API_URL_ID = ENTITY_API_URL + "/{id}"; + private static final String ENTITY_SEARCH_API_URL = "/api/_search/points"; + + private static Random random = new Random(); + private static AtomicLong count = new AtomicLong(random.nextInt() + (2 * Integer.MAX_VALUE)); + + @Autowired + private PointsRepository pointsRepository; + + @Mock + private PointsRepository pointsRepositoryMock; + + @Autowired + private PointsSearchRepository pointsSearchRepository; + + @Autowired + private EntityManager em; + + @Autowired + private MockMvc restPointsMockMvc; + + private Points points; + + /** + * Create an entity for this test. + * + * This is a static method, as tests for other entities might also need it, + * if they test an entity which requires the current entity. + */ + public static Points createEntity(EntityManager em) { + Points points = new Points() + .date(DEFAULT_DATE) + .exercise(DEFAULT_EXERCISE) + .meals(DEFAULT_MEALS) + .alcohol(DEFAULT_ALCOHOL) + .notes(DEFAULT_NOTES); + return points; + } + + /** + * Create an updated entity for this test. + * + * This is a static method, as tests for other entities might also need it, + * if they test an entity which requires the current entity. + */ + public static Points createUpdatedEntity(EntityManager em) { + Points points = new Points() + .date(UPDATED_DATE) + .exercise(UPDATED_EXERCISE) + .meals(UPDATED_MEALS) + .alcohol(UPDATED_ALCOHOL) + .notes(UPDATED_NOTES); + return points; + } + + @AfterEach + public void cleanupElasticSearchRepository() { + pointsSearchRepository.deleteAll(); + assertThat(pointsSearchRepository.count()).isEqualTo(0); + } + + @BeforeEach + public void initTest() { + points = createEntity(em); + } + + @Test + @Transactional + void createPoints() throws Exception { + int databaseSizeBeforeCreate = pointsRepository.findAll().size(); + int searchDatabaseSizeBefore = IterableUtil.sizeOf(pointsSearchRepository.findAll()); + // Create the Points + restPointsMockMvc + .perform(post(ENTITY_API_URL).contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(points))) + .andExpect(status().isCreated()); + + // Validate the Points in the database + List pointsList = pointsRepository.findAll(); + assertThat(pointsList).hasSize(databaseSizeBeforeCreate + 1); + await() + .atMost(5, TimeUnit.SECONDS) + .untilAsserted(() -> { + int searchDatabaseSizeAfter = IterableUtil.sizeOf(pointsSearchRepository.findAll()); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore + 1); + }); + Points testPoints = pointsList.get(pointsList.size() - 1); + assertThat(testPoints.getDate()).isEqualTo(DEFAULT_DATE); + assertThat(testPoints.getExercise()).isEqualTo(DEFAULT_EXERCISE); + assertThat(testPoints.getMeals()).isEqualTo(DEFAULT_MEALS); + assertThat(testPoints.getAlcohol()).isEqualTo(DEFAULT_ALCOHOL); + assertThat(testPoints.getNotes()).isEqualTo(DEFAULT_NOTES); + } + + @Test + @Transactional + void createPointsWithExistingId() throws Exception { + // Create the Points with an existing ID + points.setId(1L); + + int databaseSizeBeforeCreate = pointsRepository.findAll().size(); + int searchDatabaseSizeBefore = IterableUtil.sizeOf(pointsSearchRepository.findAll()); + + // An entity with an existing ID cannot be created, so this API call must fail + restPointsMockMvc + .perform(post(ENTITY_API_URL).contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(points))) + .andExpect(status().isBadRequest()); + + // Validate the Points in the database + List pointsList = pointsRepository.findAll(); + assertThat(pointsList).hasSize(databaseSizeBeforeCreate); + int searchDatabaseSizeAfter = IterableUtil.sizeOf(pointsSearchRepository.findAll()); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + } + + @Test + @Transactional + void checkDateIsRequired() throws Exception { + int databaseSizeBeforeTest = pointsRepository.findAll().size(); + int searchDatabaseSizeBefore = IterableUtil.sizeOf(pointsSearchRepository.findAll()); + // set the field null + points.setDate(null); + + // Create the Points, which fails. + + restPointsMockMvc + .perform(post(ENTITY_API_URL).contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(points))) + .andExpect(status().isBadRequest()); + + List pointsList = pointsRepository.findAll(); + assertThat(pointsList).hasSize(databaseSizeBeforeTest); + int searchDatabaseSizeAfter = IterableUtil.sizeOf(pointsSearchRepository.findAll()); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + } + + @Test + @Transactional + void getAllPoints() throws Exception { + // Initialize the database + pointsRepository.saveAndFlush(points); + + // Get all the pointsList + restPointsMockMvc + .perform(get(ENTITY_API_URL + "?sort=id,desc")) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(jsonPath("$.[*].id").value(hasItem(points.getId().intValue()))) + .andExpect(jsonPath("$.[*].date").value(hasItem(DEFAULT_DATE.toString()))) + .andExpect(jsonPath("$.[*].exercise").value(hasItem(DEFAULT_EXERCISE))) + .andExpect(jsonPath("$.[*].meals").value(hasItem(DEFAULT_MEALS))) + .andExpect(jsonPath("$.[*].alcohol").value(hasItem(DEFAULT_ALCOHOL))) + .andExpect(jsonPath("$.[*].notes").value(hasItem(DEFAULT_NOTES))); + } + + @SuppressWarnings({ "unchecked" }) + void getAllPointsWithEagerRelationshipsIsEnabled() throws Exception { + when(pointsRepositoryMock.findAllWithEagerRelationships(any())).thenReturn(new PageImpl(new ArrayList<>())); + + restPointsMockMvc.perform(get(ENTITY_API_URL + "?eagerload=true")).andExpect(status().isOk()); + + verify(pointsRepositoryMock, times(1)).findAllWithEagerRelationships(any()); + } + + @SuppressWarnings({ "unchecked" }) + void getAllPointsWithEagerRelationshipsIsNotEnabled() throws Exception { + when(pointsRepositoryMock.findAllWithEagerRelationships(any())).thenReturn(new PageImpl(new ArrayList<>())); + + restPointsMockMvc.perform(get(ENTITY_API_URL + "?eagerload=false")).andExpect(status().isOk()); + verify(pointsRepositoryMock, times(1)).findAll(any(Pageable.class)); + } + + @Test + @Transactional + void getPoints() throws Exception { + // Initialize the database + pointsRepository.saveAndFlush(points); + + // Get the points + restPointsMockMvc + .perform(get(ENTITY_API_URL_ID, points.getId())) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(jsonPath("$.id").value(points.getId().intValue())) + .andExpect(jsonPath("$.date").value(DEFAULT_DATE.toString())) + .andExpect(jsonPath("$.exercise").value(DEFAULT_EXERCISE)) + .andExpect(jsonPath("$.meals").value(DEFAULT_MEALS)) + .andExpect(jsonPath("$.alcohol").value(DEFAULT_ALCOHOL)) + .andExpect(jsonPath("$.notes").value(DEFAULT_NOTES)); + } + + @Test + @Transactional + void getNonExistingPoints() throws Exception { + // Get the points + restPointsMockMvc.perform(get(ENTITY_API_URL_ID, Long.MAX_VALUE)).andExpect(status().isNotFound()); + } + + @Test + @Transactional + void putExistingPoints() throws Exception { + // Initialize the database + pointsRepository.saveAndFlush(points); + + int databaseSizeBeforeUpdate = pointsRepository.findAll().size(); + pointsSearchRepository.save(points); + int searchDatabaseSizeBefore = IterableUtil.sizeOf(pointsSearchRepository.findAll()); + + // Update the points + Points updatedPoints = pointsRepository.findById(points.getId()).get(); + // Disconnect from session so that the updates on updatedPoints are not directly saved in db + em.detach(updatedPoints); + updatedPoints.date(UPDATED_DATE).exercise(UPDATED_EXERCISE).meals(UPDATED_MEALS).alcohol(UPDATED_ALCOHOL).notes(UPDATED_NOTES); + + restPointsMockMvc + .perform( + put(ENTITY_API_URL_ID, updatedPoints.getId()) + .contentType(MediaType.APPLICATION_JSON) + .content(TestUtil.convertObjectToJsonBytes(updatedPoints)) + ) + .andExpect(status().isOk()); + + // Validate the Points in the database + List pointsList = pointsRepository.findAll(); + assertThat(pointsList).hasSize(databaseSizeBeforeUpdate); + Points testPoints = pointsList.get(pointsList.size() - 1); + assertThat(testPoints.getDate()).isEqualTo(UPDATED_DATE); + assertThat(testPoints.getExercise()).isEqualTo(UPDATED_EXERCISE); + assertThat(testPoints.getMeals()).isEqualTo(UPDATED_MEALS); + assertThat(testPoints.getAlcohol()).isEqualTo(UPDATED_ALCOHOL); + assertThat(testPoints.getNotes()).isEqualTo(UPDATED_NOTES); + await() + .atMost(5, TimeUnit.SECONDS) + .untilAsserted(() -> { + int searchDatabaseSizeAfter = IterableUtil.sizeOf(pointsSearchRepository.findAll()); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + List pointsSearchList = IterableUtils.toList(pointsSearchRepository.findAll()); + Points testPointsSearch = pointsSearchList.get(searchDatabaseSizeAfter - 1); + assertThat(testPointsSearch.getDate()).isEqualTo(UPDATED_DATE); + assertThat(testPointsSearch.getExercise()).isEqualTo(UPDATED_EXERCISE); + assertThat(testPointsSearch.getMeals()).isEqualTo(UPDATED_MEALS); + assertThat(testPointsSearch.getAlcohol()).isEqualTo(UPDATED_ALCOHOL); + assertThat(testPointsSearch.getNotes()).isEqualTo(UPDATED_NOTES); + }); + } + + @Test + @Transactional + void putNonExistingPoints() throws Exception { + int databaseSizeBeforeUpdate = pointsRepository.findAll().size(); + int searchDatabaseSizeBefore = IterableUtil.sizeOf(pointsSearchRepository.findAll()); + points.setId(count.incrementAndGet()); + + // If the entity doesn't have an ID, it will throw BadRequestAlertException + restPointsMockMvc + .perform( + put(ENTITY_API_URL_ID, points.getId()) + .contentType(MediaType.APPLICATION_JSON) + .content(TestUtil.convertObjectToJsonBytes(points)) + ) + .andExpect(status().isBadRequest()); + + // Validate the Points in the database + List pointsList = pointsRepository.findAll(); + assertThat(pointsList).hasSize(databaseSizeBeforeUpdate); + int searchDatabaseSizeAfter = IterableUtil.sizeOf(pointsSearchRepository.findAll()); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + } + + @Test + @Transactional + void putWithIdMismatchPoints() throws Exception { + int databaseSizeBeforeUpdate = pointsRepository.findAll().size(); + int searchDatabaseSizeBefore = IterableUtil.sizeOf(pointsSearchRepository.findAll()); + points.setId(count.incrementAndGet()); + + // If url ID doesn't match entity ID, it will throw BadRequestAlertException + restPointsMockMvc + .perform( + put(ENTITY_API_URL_ID, count.incrementAndGet()) + .contentType(MediaType.APPLICATION_JSON) + .content(TestUtil.convertObjectToJsonBytes(points)) + ) + .andExpect(status().isBadRequest()); + + // Validate the Points in the database + List pointsList = pointsRepository.findAll(); + assertThat(pointsList).hasSize(databaseSizeBeforeUpdate); + int searchDatabaseSizeAfter = IterableUtil.sizeOf(pointsSearchRepository.findAll()); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + } + + @Test + @Transactional + void putWithMissingIdPathParamPoints() throws Exception { + int databaseSizeBeforeUpdate = pointsRepository.findAll().size(); + int searchDatabaseSizeBefore = IterableUtil.sizeOf(pointsSearchRepository.findAll()); + points.setId(count.incrementAndGet()); + + // If url ID doesn't match entity ID, it will throw BadRequestAlertException + restPointsMockMvc + .perform(put(ENTITY_API_URL).contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(points))) + .andExpect(status().isMethodNotAllowed()); + + // Validate the Points in the database + List pointsList = pointsRepository.findAll(); + assertThat(pointsList).hasSize(databaseSizeBeforeUpdate); + int searchDatabaseSizeAfter = IterableUtil.sizeOf(pointsSearchRepository.findAll()); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + } + + @Test + @Transactional + void partialUpdatePointsWithPatch() throws Exception { + // Initialize the database + pointsRepository.saveAndFlush(points); + + int databaseSizeBeforeUpdate = pointsRepository.findAll().size(); + + // Update the points using partial update + Points partialUpdatedPoints = new Points(); + partialUpdatedPoints.setId(points.getId()); + + partialUpdatedPoints.date(UPDATED_DATE).meals(UPDATED_MEALS).notes(UPDATED_NOTES); + + restPointsMockMvc + .perform( + patch(ENTITY_API_URL_ID, partialUpdatedPoints.getId()) + .contentType("application/merge-patch+json") + .content(TestUtil.convertObjectToJsonBytes(partialUpdatedPoints)) + ) + .andExpect(status().isOk()); + + // Validate the Points in the database + List pointsList = pointsRepository.findAll(); + assertThat(pointsList).hasSize(databaseSizeBeforeUpdate); + Points testPoints = pointsList.get(pointsList.size() - 1); + assertThat(testPoints.getDate()).isEqualTo(UPDATED_DATE); + assertThat(testPoints.getExercise()).isEqualTo(DEFAULT_EXERCISE); + assertThat(testPoints.getMeals()).isEqualTo(UPDATED_MEALS); + assertThat(testPoints.getAlcohol()).isEqualTo(DEFAULT_ALCOHOL); + assertThat(testPoints.getNotes()).isEqualTo(UPDATED_NOTES); + } + + @Test + @Transactional + void fullUpdatePointsWithPatch() throws Exception { + // Initialize the database + pointsRepository.saveAndFlush(points); + + int databaseSizeBeforeUpdate = pointsRepository.findAll().size(); + + // Update the points using partial update + Points partialUpdatedPoints = new Points(); + partialUpdatedPoints.setId(points.getId()); + + partialUpdatedPoints + .date(UPDATED_DATE) + .exercise(UPDATED_EXERCISE) + .meals(UPDATED_MEALS) + .alcohol(UPDATED_ALCOHOL) + .notes(UPDATED_NOTES); + + restPointsMockMvc + .perform( + patch(ENTITY_API_URL_ID, partialUpdatedPoints.getId()) + .contentType("application/merge-patch+json") + .content(TestUtil.convertObjectToJsonBytes(partialUpdatedPoints)) + ) + .andExpect(status().isOk()); + + // Validate the Points in the database + List pointsList = pointsRepository.findAll(); + assertThat(pointsList).hasSize(databaseSizeBeforeUpdate); + Points testPoints = pointsList.get(pointsList.size() - 1); + assertThat(testPoints.getDate()).isEqualTo(UPDATED_DATE); + assertThat(testPoints.getExercise()).isEqualTo(UPDATED_EXERCISE); + assertThat(testPoints.getMeals()).isEqualTo(UPDATED_MEALS); + assertThat(testPoints.getAlcohol()).isEqualTo(UPDATED_ALCOHOL); + assertThat(testPoints.getNotes()).isEqualTo(UPDATED_NOTES); + } + + @Test + @Transactional + void patchNonExistingPoints() throws Exception { + int databaseSizeBeforeUpdate = pointsRepository.findAll().size(); + int searchDatabaseSizeBefore = IterableUtil.sizeOf(pointsSearchRepository.findAll()); + points.setId(count.incrementAndGet()); + + // If the entity doesn't have an ID, it will throw BadRequestAlertException + restPointsMockMvc + .perform( + patch(ENTITY_API_URL_ID, points.getId()) + .contentType("application/merge-patch+json") + .content(TestUtil.convertObjectToJsonBytes(points)) + ) + .andExpect(status().isBadRequest()); + + // Validate the Points in the database + List pointsList = pointsRepository.findAll(); + assertThat(pointsList).hasSize(databaseSizeBeforeUpdate); + int searchDatabaseSizeAfter = IterableUtil.sizeOf(pointsSearchRepository.findAll()); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + } + + @Test + @Transactional + void patchWithIdMismatchPoints() throws Exception { + int databaseSizeBeforeUpdate = pointsRepository.findAll().size(); + int searchDatabaseSizeBefore = IterableUtil.sizeOf(pointsSearchRepository.findAll()); + points.setId(count.incrementAndGet()); + + // If url ID doesn't match entity ID, it will throw BadRequestAlertException + restPointsMockMvc + .perform( + patch(ENTITY_API_URL_ID, count.incrementAndGet()) + .contentType("application/merge-patch+json") + .content(TestUtil.convertObjectToJsonBytes(points)) + ) + .andExpect(status().isBadRequest()); + + // Validate the Points in the database + List pointsList = pointsRepository.findAll(); + assertThat(pointsList).hasSize(databaseSizeBeforeUpdate); + int searchDatabaseSizeAfter = IterableUtil.sizeOf(pointsSearchRepository.findAll()); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + } + + @Test + @Transactional + void patchWithMissingIdPathParamPoints() throws Exception { + int databaseSizeBeforeUpdate = pointsRepository.findAll().size(); + int searchDatabaseSizeBefore = IterableUtil.sizeOf(pointsSearchRepository.findAll()); + points.setId(count.incrementAndGet()); + + // If url ID doesn't match entity ID, it will throw BadRequestAlertException + restPointsMockMvc + .perform(patch(ENTITY_API_URL).contentType("application/merge-patch+json").content(TestUtil.convertObjectToJsonBytes(points))) + .andExpect(status().isMethodNotAllowed()); + + // Validate the Points in the database + List pointsList = pointsRepository.findAll(); + assertThat(pointsList).hasSize(databaseSizeBeforeUpdate); + int searchDatabaseSizeAfter = IterableUtil.sizeOf(pointsSearchRepository.findAll()); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + } + + @Test + @Transactional + void deletePoints() throws Exception { + // Initialize the database + pointsRepository.saveAndFlush(points); + pointsRepository.save(points); + pointsSearchRepository.save(points); + + int databaseSizeBeforeDelete = pointsRepository.findAll().size(); + int searchDatabaseSizeBefore = IterableUtil.sizeOf(pointsSearchRepository.findAll()); + assertThat(searchDatabaseSizeBefore).isEqualTo(databaseSizeBeforeDelete); + + // Delete the points + restPointsMockMvc + .perform(delete(ENTITY_API_URL_ID, points.getId()).accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isNoContent()); + + // Validate the database contains one less item + List pointsList = pointsRepository.findAll(); + assertThat(pointsList).hasSize(databaseSizeBeforeDelete - 1); + int searchDatabaseSizeAfter = IterableUtil.sizeOf(pointsSearchRepository.findAll()); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore - 1); + } + + @Test + @Transactional + void searchPoints() throws Exception { + // Initialize the database + points = pointsRepository.saveAndFlush(points); + pointsSearchRepository.save(points); + + // Search the points + restPointsMockMvc + .perform(get(ENTITY_SEARCH_API_URL + "?query=id:" + points.getId())) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(jsonPath("$.[*].id").value(hasItem(points.getId().intValue()))) + .andExpect(jsonPath("$.[*].date").value(hasItem(DEFAULT_DATE.toString()))) + .andExpect(jsonPath("$.[*].exercise").value(hasItem(DEFAULT_EXERCISE))) + .andExpect(jsonPath("$.[*].meals").value(hasItem(DEFAULT_MEALS))) + .andExpect(jsonPath("$.[*].alcohol").value(hasItem(DEFAULT_ALCOHOL))) + .andExpect(jsonPath("$.[*].notes").value(hasItem(DEFAULT_NOTES))); + } +} diff --git a/src/test/java/org/jhipster/health/web/rest/PreferencesResourceIT.java b/src/test/java/org/jhipster/health/web/rest/PreferencesResourceIT.java new file mode 100644 index 00000000..6fcfbb61 --- /dev/null +++ b/src/test/java/org/jhipster/health/web/rest/PreferencesResourceIT.java @@ -0,0 +1,528 @@ +package org.jhipster.health.web.rest; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; +import static org.hamcrest.Matchers.hasItem; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Random; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; +import java.util.stream.Stream; +import javax.persistence.EntityManager; +import org.apache.commons.collections4.IterableUtils; +import org.assertj.core.util.IterableUtil; +import org.jhipster.health.IntegrationTest; +import org.jhipster.health.domain.Preferences; +import org.jhipster.health.domain.enumeration.Units; +import org.jhipster.health.repository.PreferencesRepository; +import org.jhipster.health.repository.search.PreferencesSearchRepository; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.http.MediaType; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.transaction.annotation.Transactional; + +/** + * Integration tests for the {@link PreferencesResource} REST controller. + */ +@IntegrationTest +@ExtendWith(MockitoExtension.class) +@AutoConfigureMockMvc +@WithMockUser +class PreferencesResourceIT { + + private static final Integer DEFAULT_WEEKLY_GOAL = 10; + private static final Integer UPDATED_WEEKLY_GOAL = 11; + + private static final Units DEFAULT_WEIGHT_UNITS = Units.KG; + private static final Units UPDATED_WEIGHT_UNITS = Units.LB; + + private static final String ENTITY_API_URL = "/api/preferences"; + private static final String ENTITY_API_URL_ID = ENTITY_API_URL + "/{id}"; + private static final String ENTITY_SEARCH_API_URL = "/api/_search/preferences"; + + private static Random random = new Random(); + private static AtomicLong count = new AtomicLong(random.nextInt() + (2 * Integer.MAX_VALUE)); + + @Autowired + private PreferencesRepository preferencesRepository; + + @Mock + private PreferencesRepository preferencesRepositoryMock; + + @Autowired + private PreferencesSearchRepository preferencesSearchRepository; + + @Autowired + private EntityManager em; + + @Autowired + private MockMvc restPreferencesMockMvc; + + private Preferences preferences; + + /** + * Create an entity for this test. + * + * This is a static method, as tests for other entities might also need it, + * if they test an entity which requires the current entity. + */ + public static Preferences createEntity(EntityManager em) { + Preferences preferences = new Preferences().weeklyGoal(DEFAULT_WEEKLY_GOAL).weightUnits(DEFAULT_WEIGHT_UNITS); + return preferences; + } + + /** + * Create an updated entity for this test. + * + * This is a static method, as tests for other entities might also need it, + * if they test an entity which requires the current entity. + */ + public static Preferences createUpdatedEntity(EntityManager em) { + Preferences preferences = new Preferences().weeklyGoal(UPDATED_WEEKLY_GOAL).weightUnits(UPDATED_WEIGHT_UNITS); + return preferences; + } + + @AfterEach + public void cleanupElasticSearchRepository() { + preferencesSearchRepository.deleteAll(); + assertThat(preferencesSearchRepository.count()).isEqualTo(0); + } + + @BeforeEach + public void initTest() { + preferences = createEntity(em); + } + + @Test + @Transactional + void createPreferences() throws Exception { + int databaseSizeBeforeCreate = preferencesRepository.findAll().size(); + int searchDatabaseSizeBefore = IterableUtil.sizeOf(preferencesSearchRepository.findAll()); + // Create the Preferences + restPreferencesMockMvc + .perform(post(ENTITY_API_URL).contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(preferences))) + .andExpect(status().isCreated()); + + // Validate the Preferences in the database + List preferencesList = preferencesRepository.findAll(); + assertThat(preferencesList).hasSize(databaseSizeBeforeCreate + 1); + await() + .atMost(5, TimeUnit.SECONDS) + .untilAsserted(() -> { + int searchDatabaseSizeAfter = IterableUtil.sizeOf(preferencesSearchRepository.findAll()); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore + 1); + }); + Preferences testPreferences = preferencesList.get(preferencesList.size() - 1); + assertThat(testPreferences.getWeeklyGoal()).isEqualTo(DEFAULT_WEEKLY_GOAL); + assertThat(testPreferences.getWeightUnits()).isEqualTo(DEFAULT_WEIGHT_UNITS); + } + + @Test + @Transactional + void createPreferencesWithExistingId() throws Exception { + // Create the Preferences with an existing ID + preferences.setId(1L); + + int databaseSizeBeforeCreate = preferencesRepository.findAll().size(); + int searchDatabaseSizeBefore = IterableUtil.sizeOf(preferencesSearchRepository.findAll()); + + // An entity with an existing ID cannot be created, so this API call must fail + restPreferencesMockMvc + .perform(post(ENTITY_API_URL).contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(preferences))) + .andExpect(status().isBadRequest()); + + // Validate the Preferences in the database + List preferencesList = preferencesRepository.findAll(); + assertThat(preferencesList).hasSize(databaseSizeBeforeCreate); + int searchDatabaseSizeAfter = IterableUtil.sizeOf(preferencesSearchRepository.findAll()); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + } + + @Test + @Transactional + void checkWeeklyGoalIsRequired() throws Exception { + int databaseSizeBeforeTest = preferencesRepository.findAll().size(); + int searchDatabaseSizeBefore = IterableUtil.sizeOf(preferencesSearchRepository.findAll()); + // set the field null + preferences.setWeeklyGoal(null); + + // Create the Preferences, which fails. + + restPreferencesMockMvc + .perform(post(ENTITY_API_URL).contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(preferences))) + .andExpect(status().isBadRequest()); + + List preferencesList = preferencesRepository.findAll(); + assertThat(preferencesList).hasSize(databaseSizeBeforeTest); + int searchDatabaseSizeAfter = IterableUtil.sizeOf(preferencesSearchRepository.findAll()); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + } + + @Test + @Transactional + void checkWeightUnitsIsRequired() throws Exception { + int databaseSizeBeforeTest = preferencesRepository.findAll().size(); + int searchDatabaseSizeBefore = IterableUtil.sizeOf(preferencesSearchRepository.findAll()); + // set the field null + preferences.setWeightUnits(null); + + // Create the Preferences, which fails. + + restPreferencesMockMvc + .perform(post(ENTITY_API_URL).contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(preferences))) + .andExpect(status().isBadRequest()); + + List preferencesList = preferencesRepository.findAll(); + assertThat(preferencesList).hasSize(databaseSizeBeforeTest); + int searchDatabaseSizeAfter = IterableUtil.sizeOf(preferencesSearchRepository.findAll()); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + } + + @Test + @Transactional + void getAllPreferences() throws Exception { + // Initialize the database + preferencesRepository.saveAndFlush(preferences); + + // Get all the preferencesList + restPreferencesMockMvc + .perform(get(ENTITY_API_URL + "?sort=id,desc")) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(jsonPath("$.[*].id").value(hasItem(preferences.getId().intValue()))) + .andExpect(jsonPath("$.[*].weeklyGoal").value(hasItem(DEFAULT_WEEKLY_GOAL))) + .andExpect(jsonPath("$.[*].weightUnits").value(hasItem(DEFAULT_WEIGHT_UNITS.toString()))); + } + + @SuppressWarnings({ "unchecked" }) + void getAllPreferencesWithEagerRelationshipsIsEnabled() throws Exception { + when(preferencesRepositoryMock.findAllWithEagerRelationships(any())).thenReturn(new PageImpl(new ArrayList<>())); + + restPreferencesMockMvc.perform(get(ENTITY_API_URL + "?eagerload=true")).andExpect(status().isOk()); + + verify(preferencesRepositoryMock, times(1)).findAllWithEagerRelationships(any()); + } + + @SuppressWarnings({ "unchecked" }) + void getAllPreferencesWithEagerRelationshipsIsNotEnabled() throws Exception { + when(preferencesRepositoryMock.findAllWithEagerRelationships(any())).thenReturn(new PageImpl(new ArrayList<>())); + + restPreferencesMockMvc.perform(get(ENTITY_API_URL + "?eagerload=false")).andExpect(status().isOk()); + verify(preferencesRepositoryMock, times(1)).findAll(any(Pageable.class)); + } + + @Test + @Transactional + void getPreferences() throws Exception { + // Initialize the database + preferencesRepository.saveAndFlush(preferences); + + // Get the preferences + restPreferencesMockMvc + .perform(get(ENTITY_API_URL_ID, preferences.getId())) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(jsonPath("$.id").value(preferences.getId().intValue())) + .andExpect(jsonPath("$.weeklyGoal").value(DEFAULT_WEEKLY_GOAL)) + .andExpect(jsonPath("$.weightUnits").value(DEFAULT_WEIGHT_UNITS.toString())); + } + + @Test + @Transactional + void getNonExistingPreferences() throws Exception { + // Get the preferences + restPreferencesMockMvc.perform(get(ENTITY_API_URL_ID, Long.MAX_VALUE)).andExpect(status().isNotFound()); + } + + @Test + @Transactional + void putExistingPreferences() throws Exception { + // Initialize the database + preferencesRepository.saveAndFlush(preferences); + + int databaseSizeBeforeUpdate = preferencesRepository.findAll().size(); + preferencesSearchRepository.save(preferences); + int searchDatabaseSizeBefore = IterableUtil.sizeOf(preferencesSearchRepository.findAll()); + + // Update the preferences + Preferences updatedPreferences = preferencesRepository.findById(preferences.getId()).get(); + // Disconnect from session so that the updates on updatedPreferences are not directly saved in db + em.detach(updatedPreferences); + updatedPreferences.weeklyGoal(UPDATED_WEEKLY_GOAL).weightUnits(UPDATED_WEIGHT_UNITS); + + restPreferencesMockMvc + .perform( + put(ENTITY_API_URL_ID, updatedPreferences.getId()) + .contentType(MediaType.APPLICATION_JSON) + .content(TestUtil.convertObjectToJsonBytes(updatedPreferences)) + ) + .andExpect(status().isOk()); + + // Validate the Preferences in the database + List preferencesList = preferencesRepository.findAll(); + assertThat(preferencesList).hasSize(databaseSizeBeforeUpdate); + Preferences testPreferences = preferencesList.get(preferencesList.size() - 1); + assertThat(testPreferences.getWeeklyGoal()).isEqualTo(UPDATED_WEEKLY_GOAL); + assertThat(testPreferences.getWeightUnits()).isEqualTo(UPDATED_WEIGHT_UNITS); + await() + .atMost(5, TimeUnit.SECONDS) + .untilAsserted(() -> { + int searchDatabaseSizeAfter = IterableUtil.sizeOf(preferencesSearchRepository.findAll()); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + List preferencesSearchList = IterableUtils.toList(preferencesSearchRepository.findAll()); + Preferences testPreferencesSearch = preferencesSearchList.get(searchDatabaseSizeAfter - 1); + assertThat(testPreferencesSearch.getWeeklyGoal()).isEqualTo(UPDATED_WEEKLY_GOAL); + assertThat(testPreferencesSearch.getWeightUnits()).isEqualTo(UPDATED_WEIGHT_UNITS); + }); + } + + @Test + @Transactional + void putNonExistingPreferences() throws Exception { + int databaseSizeBeforeUpdate = preferencesRepository.findAll().size(); + int searchDatabaseSizeBefore = IterableUtil.sizeOf(preferencesSearchRepository.findAll()); + preferences.setId(count.incrementAndGet()); + + // If the entity doesn't have an ID, it will throw BadRequestAlertException + restPreferencesMockMvc + .perform( + put(ENTITY_API_URL_ID, preferences.getId()) + .contentType(MediaType.APPLICATION_JSON) + .content(TestUtil.convertObjectToJsonBytes(preferences)) + ) + .andExpect(status().isBadRequest()); + + // Validate the Preferences in the database + List preferencesList = preferencesRepository.findAll(); + assertThat(preferencesList).hasSize(databaseSizeBeforeUpdate); + int searchDatabaseSizeAfter = IterableUtil.sizeOf(preferencesSearchRepository.findAll()); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + } + + @Test + @Transactional + void putWithIdMismatchPreferences() throws Exception { + int databaseSizeBeforeUpdate = preferencesRepository.findAll().size(); + int searchDatabaseSizeBefore = IterableUtil.sizeOf(preferencesSearchRepository.findAll()); + preferences.setId(count.incrementAndGet()); + + // If url ID doesn't match entity ID, it will throw BadRequestAlertException + restPreferencesMockMvc + .perform( + put(ENTITY_API_URL_ID, count.incrementAndGet()) + .contentType(MediaType.APPLICATION_JSON) + .content(TestUtil.convertObjectToJsonBytes(preferences)) + ) + .andExpect(status().isBadRequest()); + + // Validate the Preferences in the database + List preferencesList = preferencesRepository.findAll(); + assertThat(preferencesList).hasSize(databaseSizeBeforeUpdate); + int searchDatabaseSizeAfter = IterableUtil.sizeOf(preferencesSearchRepository.findAll()); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + } + + @Test + @Transactional + void putWithMissingIdPathParamPreferences() throws Exception { + int databaseSizeBeforeUpdate = preferencesRepository.findAll().size(); + int searchDatabaseSizeBefore = IterableUtil.sizeOf(preferencesSearchRepository.findAll()); + preferences.setId(count.incrementAndGet()); + + // If url ID doesn't match entity ID, it will throw BadRequestAlertException + restPreferencesMockMvc + .perform(put(ENTITY_API_URL).contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(preferences))) + .andExpect(status().isMethodNotAllowed()); + + // Validate the Preferences in the database + List preferencesList = preferencesRepository.findAll(); + assertThat(preferencesList).hasSize(databaseSizeBeforeUpdate); + int searchDatabaseSizeAfter = IterableUtil.sizeOf(preferencesSearchRepository.findAll()); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + } + + @Test + @Transactional + void partialUpdatePreferencesWithPatch() throws Exception { + // Initialize the database + preferencesRepository.saveAndFlush(preferences); + + int databaseSizeBeforeUpdate = preferencesRepository.findAll().size(); + + // Update the preferences using partial update + Preferences partialUpdatedPreferences = new Preferences(); + partialUpdatedPreferences.setId(preferences.getId()); + + partialUpdatedPreferences.weightUnits(UPDATED_WEIGHT_UNITS); + + restPreferencesMockMvc + .perform( + patch(ENTITY_API_URL_ID, partialUpdatedPreferences.getId()) + .contentType("application/merge-patch+json") + .content(TestUtil.convertObjectToJsonBytes(partialUpdatedPreferences)) + ) + .andExpect(status().isOk()); + + // Validate the Preferences in the database + List preferencesList = preferencesRepository.findAll(); + assertThat(preferencesList).hasSize(databaseSizeBeforeUpdate); + Preferences testPreferences = preferencesList.get(preferencesList.size() - 1); + assertThat(testPreferences.getWeeklyGoal()).isEqualTo(DEFAULT_WEEKLY_GOAL); + assertThat(testPreferences.getWeightUnits()).isEqualTo(UPDATED_WEIGHT_UNITS); + } + + @Test + @Transactional + void fullUpdatePreferencesWithPatch() throws Exception { + // Initialize the database + preferencesRepository.saveAndFlush(preferences); + + int databaseSizeBeforeUpdate = preferencesRepository.findAll().size(); + + // Update the preferences using partial update + Preferences partialUpdatedPreferences = new Preferences(); + partialUpdatedPreferences.setId(preferences.getId()); + + partialUpdatedPreferences.weeklyGoal(UPDATED_WEEKLY_GOAL).weightUnits(UPDATED_WEIGHT_UNITS); + + restPreferencesMockMvc + .perform( + patch(ENTITY_API_URL_ID, partialUpdatedPreferences.getId()) + .contentType("application/merge-patch+json") + .content(TestUtil.convertObjectToJsonBytes(partialUpdatedPreferences)) + ) + .andExpect(status().isOk()); + + // Validate the Preferences in the database + List preferencesList = preferencesRepository.findAll(); + assertThat(preferencesList).hasSize(databaseSizeBeforeUpdate); + Preferences testPreferences = preferencesList.get(preferencesList.size() - 1); + assertThat(testPreferences.getWeeklyGoal()).isEqualTo(UPDATED_WEEKLY_GOAL); + assertThat(testPreferences.getWeightUnits()).isEqualTo(UPDATED_WEIGHT_UNITS); + } + + @Test + @Transactional + void patchNonExistingPreferences() throws Exception { + int databaseSizeBeforeUpdate = preferencesRepository.findAll().size(); + int searchDatabaseSizeBefore = IterableUtil.sizeOf(preferencesSearchRepository.findAll()); + preferences.setId(count.incrementAndGet()); + + // If the entity doesn't have an ID, it will throw BadRequestAlertException + restPreferencesMockMvc + .perform( + patch(ENTITY_API_URL_ID, preferences.getId()) + .contentType("application/merge-patch+json") + .content(TestUtil.convertObjectToJsonBytes(preferences)) + ) + .andExpect(status().isBadRequest()); + + // Validate the Preferences in the database + List preferencesList = preferencesRepository.findAll(); + assertThat(preferencesList).hasSize(databaseSizeBeforeUpdate); + int searchDatabaseSizeAfter = IterableUtil.sizeOf(preferencesSearchRepository.findAll()); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + } + + @Test + @Transactional + void patchWithIdMismatchPreferences() throws Exception { + int databaseSizeBeforeUpdate = preferencesRepository.findAll().size(); + int searchDatabaseSizeBefore = IterableUtil.sizeOf(preferencesSearchRepository.findAll()); + preferences.setId(count.incrementAndGet()); + + // If url ID doesn't match entity ID, it will throw BadRequestAlertException + restPreferencesMockMvc + .perform( + patch(ENTITY_API_URL_ID, count.incrementAndGet()) + .contentType("application/merge-patch+json") + .content(TestUtil.convertObjectToJsonBytes(preferences)) + ) + .andExpect(status().isBadRequest()); + + // Validate the Preferences in the database + List preferencesList = preferencesRepository.findAll(); + assertThat(preferencesList).hasSize(databaseSizeBeforeUpdate); + int searchDatabaseSizeAfter = IterableUtil.sizeOf(preferencesSearchRepository.findAll()); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + } + + @Test + @Transactional + void patchWithMissingIdPathParamPreferences() throws Exception { + int databaseSizeBeforeUpdate = preferencesRepository.findAll().size(); + int searchDatabaseSizeBefore = IterableUtil.sizeOf(preferencesSearchRepository.findAll()); + preferences.setId(count.incrementAndGet()); + + // If url ID doesn't match entity ID, it will throw BadRequestAlertException + restPreferencesMockMvc + .perform( + patch(ENTITY_API_URL).contentType("application/merge-patch+json").content(TestUtil.convertObjectToJsonBytes(preferences)) + ) + .andExpect(status().isMethodNotAllowed()); + + // Validate the Preferences in the database + List preferencesList = preferencesRepository.findAll(); + assertThat(preferencesList).hasSize(databaseSizeBeforeUpdate); + int searchDatabaseSizeAfter = IterableUtil.sizeOf(preferencesSearchRepository.findAll()); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + } + + @Test + @Transactional + void deletePreferences() throws Exception { + // Initialize the database + preferencesRepository.saveAndFlush(preferences); + preferencesRepository.save(preferences); + preferencesSearchRepository.save(preferences); + + int databaseSizeBeforeDelete = preferencesRepository.findAll().size(); + int searchDatabaseSizeBefore = IterableUtil.sizeOf(preferencesSearchRepository.findAll()); + assertThat(searchDatabaseSizeBefore).isEqualTo(databaseSizeBeforeDelete); + + // Delete the preferences + restPreferencesMockMvc + .perform(delete(ENTITY_API_URL_ID, preferences.getId()).accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isNoContent()); + + // Validate the database contains one less item + List preferencesList = preferencesRepository.findAll(); + assertThat(preferencesList).hasSize(databaseSizeBeforeDelete - 1); + int searchDatabaseSizeAfter = IterableUtil.sizeOf(preferencesSearchRepository.findAll()); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore - 1); + } + + @Test + @Transactional + void searchPreferences() throws Exception { + // Initialize the database + preferences = preferencesRepository.saveAndFlush(preferences); + preferencesSearchRepository.save(preferences); + + // Search the preferences + restPreferencesMockMvc + .perform(get(ENTITY_SEARCH_API_URL + "?query=id:" + preferences.getId())) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(jsonPath("$.[*].id").value(hasItem(preferences.getId().intValue()))) + .andExpect(jsonPath("$.[*].weeklyGoal").value(hasItem(DEFAULT_WEEKLY_GOAL))) + .andExpect(jsonPath("$.[*].weightUnits").value(hasItem(DEFAULT_WEIGHT_UNITS.toString()))); + } +} diff --git a/src/test/java/org/jhipster/health/web/rest/PublicUserResourceIT.java b/src/test/java/org/jhipster/health/web/rest/PublicUserResourceIT.java new file mode 100644 index 00000000..8eac6b7f --- /dev/null +++ b/src/test/java/org/jhipster/health/web/rest/PublicUserResourceIT.java @@ -0,0 +1,108 @@ +package org.jhipster.health.web.rest; + +import static org.hamcrest.Matchers.hasItem; +import static org.hamcrest.Matchers.hasItems; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import javax.persistence.EntityManager; +import org.jhipster.health.IntegrationTest; +import org.jhipster.health.domain.User; +import org.jhipster.health.repository.UserRepository; +import org.jhipster.health.repository.search.UserSearchRepository; +import org.jhipster.health.security.AuthoritiesConstants; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.cache.CacheManager; +import org.springframework.http.MediaType; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.transaction.annotation.Transactional; + +/** + * Integration tests for the {@link UserResource} REST controller. + */ +@AutoConfigureMockMvc +@WithMockUser(authorities = AuthoritiesConstants.ADMIN) +@IntegrationTest +class PublicUserResourceIT { + + private static final String DEFAULT_LOGIN = "johndoe"; + + @Autowired + private UserRepository userRepository; + + /** + * This repository is mocked in the org.jhipster.health.repository.search test package. + * + * @see org.jhipster.health.repository.search.UserSearchRepositoryMockConfiguration + */ + @Autowired + private UserSearchRepository mockUserSearchRepository; + + @Autowired + private EntityManager em; + + @Autowired + private CacheManager cacheManager; + + @Autowired + private MockMvc restUserMockMvc; + + private User user; + + @BeforeEach + public void setup() { + cacheManager.getCache(UserRepository.USERS_BY_LOGIN_CACHE).clear(); + cacheManager.getCache(UserRepository.USERS_BY_EMAIL_CACHE).clear(); + } + + @BeforeEach + public void initTest() { + user = UserResourceIT.initTestUser(userRepository, em); + } + + @Test + @Transactional + void getAllPublicUsers() throws Exception { + // Initialize the database + userRepository.saveAndFlush(user); + + // Get all the users + restUserMockMvc + .perform(get("/api/users?sort=id,desc").accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(jsonPath("$.[*].login").value(hasItem(DEFAULT_LOGIN))) + .andExpect(jsonPath("$.[*].email").doesNotExist()) + .andExpect(jsonPath("$.[*].imageUrl").doesNotExist()) + .andExpect(jsonPath("$.[*].langKey").doesNotExist()); + } + + @Test + @Transactional + void getAllAuthorities() throws Exception { + restUserMockMvc + .perform(get("/api/authorities").accept(MediaType.APPLICATION_JSON).contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(jsonPath("$").isArray()) + .andExpect(jsonPath("$").value(hasItems(AuthoritiesConstants.USER, AuthoritiesConstants.ADMIN))); + } + + @Test + @Transactional + void getAllUsersSortedByParameters() throws Exception { + // Initialize the database + userRepository.saveAndFlush(user); + + restUserMockMvc.perform(get("/api/users?sort=resetKey,desc").accept(MediaType.APPLICATION_JSON)).andExpect(status().isBadRequest()); + restUserMockMvc.perform(get("/api/users?sort=password,desc").accept(MediaType.APPLICATION_JSON)).andExpect(status().isBadRequest()); + restUserMockMvc + .perform(get("/api/users?sort=resetKey,desc&sort=id,desc").accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isBadRequest()); + restUserMockMvc.perform(get("/api/users?sort=id,desc").accept(MediaType.APPLICATION_JSON)).andExpect(status().isOk()); + } +} diff --git a/src/test/java/org/jhipster/health/web/rest/TestUtil.java b/src/test/java/org/jhipster/health/web/rest/TestUtil.java new file mode 100644 index 00000000..f357141b --- /dev/null +++ b/src/test/java/org/jhipster/health/web/rest/TestUtil.java @@ -0,0 +1,206 @@ +package org.jhipster.health.web.rest; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import java.io.IOException; +import java.math.BigDecimal; +import java.time.ZonedDateTime; +import java.time.format.DateTimeParseException; +import java.util.List; +import javax.persistence.EntityManager; +import javax.persistence.TypedQuery; +import javax.persistence.criteria.CriteriaBuilder; +import javax.persistence.criteria.CriteriaQuery; +import javax.persistence.criteria.Root; +import org.hamcrest.Description; +import org.hamcrest.TypeSafeDiagnosingMatcher; +import org.hamcrest.TypeSafeMatcher; +import org.springframework.format.datetime.standard.DateTimeFormatterRegistrar; +import org.springframework.format.support.DefaultFormattingConversionService; +import org.springframework.format.support.FormattingConversionService; + +/** + * Utility class for testing REST controllers. + */ +public final class TestUtil { + + private static final ObjectMapper mapper = createObjectMapper(); + + private static ObjectMapper createObjectMapper() { + ObjectMapper mapper = new ObjectMapper(); + mapper.configure(SerializationFeature.WRITE_DURATIONS_AS_TIMESTAMPS, false); + mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY); + mapper.registerModule(new JavaTimeModule()); + return mapper; + } + + /** + * Convert an object to JSON byte array. + * + * @param object the object to convert. + * @return the JSON byte array. + * @throws IOException + */ + public static byte[] convertObjectToJsonBytes(Object object) throws IOException { + return mapper.writeValueAsBytes(object); + } + + /** + * Create a byte array with a specific size filled with specified data. + * + * @param size the size of the byte array. + * @param data the data to put in the byte array. + * @return the JSON byte array. + */ + public static byte[] createByteArray(int size, String data) { + byte[] byteArray = new byte[size]; + for (int i = 0; i < size; i++) { + byteArray[i] = Byte.parseByte(data, 2); + } + return byteArray; + } + + /** + * A matcher that tests that the examined string represents the same instant as the reference datetime. + */ + public static class ZonedDateTimeMatcher extends TypeSafeDiagnosingMatcher { + + private final ZonedDateTime date; + + public ZonedDateTimeMatcher(ZonedDateTime date) { + this.date = date; + } + + @Override + protected boolean matchesSafely(String item, Description mismatchDescription) { + try { + if (!date.isEqual(ZonedDateTime.parse(item))) { + mismatchDescription.appendText("was ").appendValue(item); + return false; + } + return true; + } catch (DateTimeParseException e) { + mismatchDescription.appendText("was ").appendValue(item).appendText(", which could not be parsed as a ZonedDateTime"); + return false; + } + } + + @Override + public void describeTo(Description description) { + description.appendText("a String representing the same Instant as ").appendValue(date); + } + } + + /** + * Creates a matcher that matches when the examined string represents the same instant as the reference datetime. + * + * @param date the reference datetime against which the examined string is checked. + */ + public static ZonedDateTimeMatcher sameInstant(ZonedDateTime date) { + return new ZonedDateTimeMatcher(date); + } + + /** + * A matcher that tests that the examined number represents the same value - it can be Long, Double, etc - as the reference BigDecimal. + */ + public static class NumberMatcher extends TypeSafeMatcher { + + final BigDecimal value; + + public NumberMatcher(BigDecimal value) { + this.value = value; + } + + @Override + public void describeTo(Description description) { + description.appendText("a numeric value is ").appendValue(value); + } + + @Override + protected boolean matchesSafely(Number item) { + BigDecimal bigDecimal = asDecimal(item); + return bigDecimal != null && value.compareTo(bigDecimal) == 0; + } + + private static BigDecimal asDecimal(Number item) { + if (item == null) { + return null; + } + if (item instanceof BigDecimal) { + return (BigDecimal) item; + } else if (item instanceof Long) { + return BigDecimal.valueOf((Long) item); + } else if (item instanceof Integer) { + return BigDecimal.valueOf((Integer) item); + } else if (item instanceof Double) { + return BigDecimal.valueOf((Double) item); + } else if (item instanceof Float) { + return BigDecimal.valueOf((Float) item); + } else { + return BigDecimal.valueOf(item.doubleValue()); + } + } + } + + /** + * Creates a matcher that matches when the examined number represents the same value as the reference BigDecimal. + * + * @param number the reference BigDecimal against which the examined number is checked. + */ + public static NumberMatcher sameNumber(BigDecimal number) { + return new NumberMatcher(number); + } + + /** + * Verifies the equals/hashcode contract on the domain object. + */ + public static void equalsVerifier(Class clazz) throws Exception { + T domainObject1 = clazz.getConstructor().newInstance(); + assertThat(domainObject1.toString()).isNotNull(); + assertThat(domainObject1).isEqualTo(domainObject1); + assertThat(domainObject1).hasSameHashCodeAs(domainObject1); + // Test with an instance of another class + Object testOtherObject = new Object(); + assertThat(domainObject1).isNotEqualTo(testOtherObject); + assertThat(domainObject1).isNotEqualTo(null); + // Test with an instance of the same class + T domainObject2 = clazz.getConstructor().newInstance(); + assertThat(domainObject1).isNotEqualTo(domainObject2); + // HashCodes are equals because the objects are not persisted yet + assertThat(domainObject1).hasSameHashCodeAs(domainObject2); + } + + /** + * Create a {@link FormattingConversionService} which use ISO date format, instead of the localized one. + * @return the {@link FormattingConversionService}. + */ + public static FormattingConversionService createFormattingConversionService() { + DefaultFormattingConversionService dfcs = new DefaultFormattingConversionService(); + DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar(); + registrar.setUseIsoFormat(true); + registrar.registerFormatters(dfcs); + return dfcs; + } + + /** + * Makes a an executes a query to the EntityManager finding all stored objects. + * @param The type of objects to be searched + * @param em The instance of the EntityManager + * @param clss The class type to be searched + * @return A list of all found objects + */ + public static List findAll(EntityManager em, Class clss) { + CriteriaBuilder cb = em.getCriteriaBuilder(); + CriteriaQuery cq = cb.createQuery(clss); + Root rootEntry = cq.from(clss); + CriteriaQuery all = cq.select(rootEntry); + TypedQuery allQuery = em.createQuery(all); + return allQuery.getResultList(); + } + + private TestUtil() {} +} diff --git a/src/test/java/org/jhipster/health/web/rest/UserJWTControllerIT.java b/src/test/java/org/jhipster/health/web/rest/UserJWTControllerIT.java new file mode 100644 index 00000000..fc08d02f --- /dev/null +++ b/src/test/java/org/jhipster/health/web/rest/UserJWTControllerIT.java @@ -0,0 +1,98 @@ +package org.jhipster.health.web.rest; + +import static org.hamcrest.Matchers.emptyString; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.nullValue; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import org.jhipster.health.IntegrationTest; +import org.jhipster.health.domain.User; +import org.jhipster.health.repository.UserRepository; +import org.jhipster.health.web.rest.vm.LoginVM; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.http.MediaType; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.transaction.annotation.Transactional; + +/** + * Integration tests for the {@link UserJWTController} REST controller. + */ +@AutoConfigureMockMvc +@IntegrationTest +class UserJWTControllerIT { + + @Autowired + private UserRepository userRepository; + + @Autowired + private PasswordEncoder passwordEncoder; + + @Autowired + private MockMvc mockMvc; + + @Test + @Transactional + void testAuthorize() throws Exception { + User user = new User(); + user.setLogin("user-jwt-controller"); + user.setEmail("user-jwt-controller@example.com"); + user.setActivated(true); + user.setPassword(passwordEncoder.encode("test")); + + userRepository.saveAndFlush(user); + + LoginVM login = new LoginVM(); + login.setUsername("user-jwt-controller"); + login.setPassword("test"); + mockMvc + .perform(post("/api/authenticate").contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(login))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id_token").isString()) + .andExpect(jsonPath("$.id_token").isNotEmpty()) + .andExpect(header().string("Authorization", not(nullValue()))) + .andExpect(header().string("Authorization", not(is(emptyString())))); + } + + @Test + @Transactional + void testAuthorizeWithRememberMe() throws Exception { + User user = new User(); + user.setLogin("user-jwt-controller-remember-me"); + user.setEmail("user-jwt-controller-remember-me@example.com"); + user.setActivated(true); + user.setPassword(passwordEncoder.encode("test")); + + userRepository.saveAndFlush(user); + + LoginVM login = new LoginVM(); + login.setUsername("user-jwt-controller-remember-me"); + login.setPassword("test"); + login.setRememberMe(true); + mockMvc + .perform(post("/api/authenticate").contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(login))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.id_token").isString()) + .andExpect(jsonPath("$.id_token").isNotEmpty()) + .andExpect(header().string("Authorization", not(nullValue()))) + .andExpect(header().string("Authorization", not(is(emptyString())))); + } + + @Test + void testAuthorizeFails() throws Exception { + LoginVM login = new LoginVM(); + login.setUsername("wrong-user"); + login.setPassword("wrong password"); + mockMvc + .perform(post("/api/authenticate").contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(login))) + .andExpect(status().isUnauthorized()) + .andExpect(jsonPath("$.id_token").doesNotExist()) + .andExpect(header().doesNotExist("Authorization")); + } +} diff --git a/src/test/java/org/jhipster/health/web/rest/UserResourceIT.java b/src/test/java/org/jhipster/health/web/rest/UserResourceIT.java new file mode 100644 index 00000000..a03cf5bd --- /dev/null +++ b/src/test/java/org/jhipster/health/web/rest/UserResourceIT.java @@ -0,0 +1,614 @@ +package org.jhipster.health.web.rest; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.Matchers.hasItem; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import java.time.Instant; +import java.util.*; +import java.util.function.Consumer; +import javax.persistence.EntityManager; +import org.apache.commons.lang3.RandomStringUtils; +import org.jhipster.health.IntegrationTest; +import org.jhipster.health.domain.Authority; +import org.jhipster.health.domain.User; +import org.jhipster.health.repository.UserRepository; +import org.jhipster.health.repository.search.UserSearchRepository; +import org.jhipster.health.security.AuthoritiesConstants; +import org.jhipster.health.service.dto.AdminUserDTO; +import org.jhipster.health.service.mapper.UserMapper; +import org.jhipster.health.web.rest.vm.ManagedUserVM; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.cache.CacheManager; +import org.springframework.http.MediaType; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.transaction.annotation.Transactional; + +/** + * Integration tests for the {@link UserResource} REST controller. + */ +@AutoConfigureMockMvc +@WithMockUser(authorities = AuthoritiesConstants.ADMIN) +@IntegrationTest +class UserResourceIT { + + private static final String DEFAULT_LOGIN = "johndoe"; + private static final String UPDATED_LOGIN = "jhipster"; + + private static final Long DEFAULT_ID = 1L; + + private static final String DEFAULT_PASSWORD = "passjohndoe"; + private static final String UPDATED_PASSWORD = "passjhipster"; + + private static final String DEFAULT_EMAIL = "johndoe@localhost"; + private static final String UPDATED_EMAIL = "jhipster@localhost"; + + private static final String DEFAULT_FIRSTNAME = "john"; + private static final String UPDATED_FIRSTNAME = "jhipsterFirstName"; + + private static final String DEFAULT_LASTNAME = "doe"; + private static final String UPDATED_LASTNAME = "jhipsterLastName"; + + private static final String DEFAULT_IMAGEURL = "http://placehold.it/50x50"; + private static final String UPDATED_IMAGEURL = "http://placehold.it/40x40"; + + private static final String DEFAULT_LANGKEY = "en"; + private static final String UPDATED_LANGKEY = "fr"; + + @Autowired + private UserRepository userRepository; + + @Autowired + private UserSearchRepository userSearchRepository; + + @Autowired + private UserMapper userMapper; + + @Autowired + private EntityManager em; + + @Autowired + private CacheManager cacheManager; + + @Autowired + private MockMvc restUserMockMvc; + + private User user; + + @BeforeEach + public void setup() { + cacheManager.getCache(UserRepository.USERS_BY_LOGIN_CACHE).clear(); + cacheManager.getCache(UserRepository.USERS_BY_EMAIL_CACHE).clear(); + } + + /** + * Create a User. + * + * This is a static method, as tests for other entities might also need it, + * if they test an entity which has a required relationship to the User entity. + */ + public static User createEntity(EntityManager em) { + User user = new User(); + user.setLogin(DEFAULT_LOGIN + RandomStringUtils.randomAlphabetic(5)); + user.setPassword(RandomStringUtils.randomAlphanumeric(60)); + user.setActivated(true); + user.setEmail(RandomStringUtils.randomAlphabetic(5) + DEFAULT_EMAIL); + user.setFirstName(DEFAULT_FIRSTNAME); + user.setLastName(DEFAULT_LASTNAME); + user.setImageUrl(DEFAULT_IMAGEURL); + user.setLangKey(DEFAULT_LANGKEY); + return user; + } + + /** + * Setups the database with one user. + */ + public static User initTestUser(UserRepository userRepository, EntityManager em) { + userRepository.deleteAll(); + User user = createEntity(em); + user.setLogin(DEFAULT_LOGIN); + user.setEmail(DEFAULT_EMAIL); + return user; + } + + @BeforeEach + public void initTest() { + user = initTestUser(userRepository, em); + } + + @Test + @Transactional + void createUser() throws Exception { + int databaseSizeBeforeCreate = userRepository.findAll().size(); + + // Create the User + ManagedUserVM managedUserVM = new ManagedUserVM(); + managedUserVM.setLogin(DEFAULT_LOGIN); + managedUserVM.setPassword(DEFAULT_PASSWORD); + managedUserVM.setFirstName(DEFAULT_FIRSTNAME); + managedUserVM.setLastName(DEFAULT_LASTNAME); + managedUserVM.setEmail(DEFAULT_EMAIL); + managedUserVM.setActivated(true); + managedUserVM.setImageUrl(DEFAULT_IMAGEURL); + managedUserVM.setLangKey(DEFAULT_LANGKEY); + managedUserVM.setAuthorities(Collections.singleton(AuthoritiesConstants.USER)); + + restUserMockMvc + .perform( + post("/api/admin/users").contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(managedUserVM)) + ) + .andExpect(status().isCreated()); + + // Validate the User in the database + assertPersistedUsers(users -> { + assertThat(users).hasSize(databaseSizeBeforeCreate + 1); + User testUser = users.get(users.size() - 1); + assertThat(testUser.getLogin()).isEqualTo(DEFAULT_LOGIN); + assertThat(testUser.getFirstName()).isEqualTo(DEFAULT_FIRSTNAME); + assertThat(testUser.getLastName()).isEqualTo(DEFAULT_LASTNAME); + assertThat(testUser.getEmail()).isEqualTo(DEFAULT_EMAIL); + assertThat(testUser.getImageUrl()).isEqualTo(DEFAULT_IMAGEURL); + assertThat(testUser.getLangKey()).isEqualTo(DEFAULT_LANGKEY); + }); + + userSearchRepository.deleteAll(); + } + + @Test + @Transactional + void createUserWithExistingId() throws Exception { + int databaseSizeBeforeCreate = userRepository.findAll().size(); + + ManagedUserVM managedUserVM = new ManagedUserVM(); + managedUserVM.setId(DEFAULT_ID); + managedUserVM.setLogin(DEFAULT_LOGIN); + managedUserVM.setPassword(DEFAULT_PASSWORD); + managedUserVM.setFirstName(DEFAULT_FIRSTNAME); + managedUserVM.setLastName(DEFAULT_LASTNAME); + managedUserVM.setEmail(DEFAULT_EMAIL); + managedUserVM.setActivated(true); + managedUserVM.setImageUrl(DEFAULT_IMAGEURL); + managedUserVM.setLangKey(DEFAULT_LANGKEY); + managedUserVM.setAuthorities(Collections.singleton(AuthoritiesConstants.USER)); + + // An entity with an existing ID cannot be created, so this API call must fail + restUserMockMvc + .perform( + post("/api/admin/users").contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(managedUserVM)) + ) + .andExpect(status().isBadRequest()); + + // Validate the User in the database + assertPersistedUsers(users -> assertThat(users).hasSize(databaseSizeBeforeCreate)); + + userSearchRepository.deleteAll(); + } + + @Test + @Transactional + void createUserWithExistingLogin() throws Exception { + // Initialize the database + userRepository.saveAndFlush(user); + userSearchRepository.save(user); + int databaseSizeBeforeCreate = userRepository.findAll().size(); + + ManagedUserVM managedUserVM = new ManagedUserVM(); + managedUserVM.setLogin(DEFAULT_LOGIN); // this login should already be used + managedUserVM.setPassword(DEFAULT_PASSWORD); + managedUserVM.setFirstName(DEFAULT_FIRSTNAME); + managedUserVM.setLastName(DEFAULT_LASTNAME); + managedUserVM.setEmail("anothermail@localhost"); + managedUserVM.setActivated(true); + managedUserVM.setImageUrl(DEFAULT_IMAGEURL); + managedUserVM.setLangKey(DEFAULT_LANGKEY); + managedUserVM.setAuthorities(Collections.singleton(AuthoritiesConstants.USER)); + + // Create the User + restUserMockMvc + .perform( + post("/api/admin/users").contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(managedUserVM)) + ) + .andExpect(status().isBadRequest()); + + // Validate the User in the database + assertPersistedUsers(users -> assertThat(users).hasSize(databaseSizeBeforeCreate)); + + userSearchRepository.deleteAll(); + } + + @Test + @Transactional + void createUserWithExistingEmail() throws Exception { + // Initialize the database + userRepository.saveAndFlush(user); + userSearchRepository.save(user); + int databaseSizeBeforeCreate = userRepository.findAll().size(); + + ManagedUserVM managedUserVM = new ManagedUserVM(); + managedUserVM.setLogin("anotherlogin"); + managedUserVM.setPassword(DEFAULT_PASSWORD); + managedUserVM.setFirstName(DEFAULT_FIRSTNAME); + managedUserVM.setLastName(DEFAULT_LASTNAME); + managedUserVM.setEmail(DEFAULT_EMAIL); // this email should already be used + managedUserVM.setActivated(true); + managedUserVM.setImageUrl(DEFAULT_IMAGEURL); + managedUserVM.setLangKey(DEFAULT_LANGKEY); + managedUserVM.setAuthorities(Collections.singleton(AuthoritiesConstants.USER)); + + // Create the User + restUserMockMvc + .perform( + post("/api/admin/users").contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(managedUserVM)) + ) + .andExpect(status().isBadRequest()); + + // Validate the User in the database + assertPersistedUsers(users -> assertThat(users).hasSize(databaseSizeBeforeCreate)); + + userSearchRepository.deleteAll(); + } + + @Test + @Transactional + void getAllUsers() throws Exception { + // Initialize the database + userRepository.saveAndFlush(user); + + // Get all the users + restUserMockMvc + .perform(get("/api/admin/users?sort=id,desc").accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(jsonPath("$.[*].login").value(hasItem(DEFAULT_LOGIN))) + .andExpect(jsonPath("$.[*].firstName").value(hasItem(DEFAULT_FIRSTNAME))) + .andExpect(jsonPath("$.[*].lastName").value(hasItem(DEFAULT_LASTNAME))) + .andExpect(jsonPath("$.[*].email").value(hasItem(DEFAULT_EMAIL))) + .andExpect(jsonPath("$.[*].imageUrl").value(hasItem(DEFAULT_IMAGEURL))) + .andExpect(jsonPath("$.[*].langKey").value(hasItem(DEFAULT_LANGKEY))); + + userSearchRepository.deleteAll(); + } + + @Test + @Transactional + void getUser() throws Exception { + // Initialize the database + userRepository.saveAndFlush(user); + + userSearchRepository.save(user); + + assertThat(cacheManager.getCache(UserRepository.USERS_BY_LOGIN_CACHE).get(user.getLogin())).isNull(); + + // Get the user + restUserMockMvc + .perform(get("/api/admin/users/{login}", user.getLogin())) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(jsonPath("$.login").value(user.getLogin())) + .andExpect(jsonPath("$.firstName").value(DEFAULT_FIRSTNAME)) + .andExpect(jsonPath("$.lastName").value(DEFAULT_LASTNAME)) + .andExpect(jsonPath("$.email").value(DEFAULT_EMAIL)) + .andExpect(jsonPath("$.imageUrl").value(DEFAULT_IMAGEURL)) + .andExpect(jsonPath("$.langKey").value(DEFAULT_LANGKEY)); + + assertThat(cacheManager.getCache(UserRepository.USERS_BY_LOGIN_CACHE).get(user.getLogin())).isNotNull(); + + userSearchRepository.deleteAll(); + } + + @Test + @Transactional + void getNonExistingUser() throws Exception { + restUserMockMvc.perform(get("/api/admin/users/unknown")).andExpect(status().isNotFound()); + } + + @Test + @Transactional + void updateUser() throws Exception { + // Initialize the database + userRepository.saveAndFlush(user); + int databaseSizeBeforeUpdate = userRepository.findAll().size(); + + // Update the user + User updatedUser = userRepository.findById(user.getId()).get(); + + ManagedUserVM managedUserVM = new ManagedUserVM(); + managedUserVM.setId(updatedUser.getId()); + managedUserVM.setLogin(updatedUser.getLogin()); + managedUserVM.setPassword(UPDATED_PASSWORD); + managedUserVM.setFirstName(UPDATED_FIRSTNAME); + managedUserVM.setLastName(UPDATED_LASTNAME); + managedUserVM.setEmail(UPDATED_EMAIL); + managedUserVM.setActivated(updatedUser.isActivated()); + managedUserVM.setImageUrl(UPDATED_IMAGEURL); + managedUserVM.setLangKey(UPDATED_LANGKEY); + managedUserVM.setCreatedBy(updatedUser.getCreatedBy()); + managedUserVM.setCreatedDate(updatedUser.getCreatedDate()); + managedUserVM.setLastModifiedBy(updatedUser.getLastModifiedBy()); + managedUserVM.setLastModifiedDate(updatedUser.getLastModifiedDate()); + managedUserVM.setAuthorities(Collections.singleton(AuthoritiesConstants.USER)); + + restUserMockMvc + .perform( + put("/api/admin/users").contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(managedUserVM)) + ) + .andExpect(status().isOk()); + + // Validate the User in the database + assertPersistedUsers(users -> { + assertThat(users).hasSize(databaseSizeBeforeUpdate); + User testUser = users.stream().filter(usr -> usr.getId().equals(updatedUser.getId())).findFirst().get(); + assertThat(testUser.getFirstName()).isEqualTo(UPDATED_FIRSTNAME); + assertThat(testUser.getLastName()).isEqualTo(UPDATED_LASTNAME); + assertThat(testUser.getEmail()).isEqualTo(UPDATED_EMAIL); + assertThat(testUser.getImageUrl()).isEqualTo(UPDATED_IMAGEURL); + assertThat(testUser.getLangKey()).isEqualTo(UPDATED_LANGKEY); + }); + + userSearchRepository.deleteAll(); + } + + @Test + @Transactional + void updateUserLogin() throws Exception { + // Initialize the database + userRepository.saveAndFlush(user); + int databaseSizeBeforeUpdate = userRepository.findAll().size(); + + // Update the user + User updatedUser = userRepository.findById(user.getId()).get(); + + ManagedUserVM managedUserVM = new ManagedUserVM(); + managedUserVM.setId(updatedUser.getId()); + managedUserVM.setLogin(UPDATED_LOGIN); + managedUserVM.setPassword(UPDATED_PASSWORD); + managedUserVM.setFirstName(UPDATED_FIRSTNAME); + managedUserVM.setLastName(UPDATED_LASTNAME); + managedUserVM.setEmail(UPDATED_EMAIL); + managedUserVM.setActivated(updatedUser.isActivated()); + managedUserVM.setImageUrl(UPDATED_IMAGEURL); + managedUserVM.setLangKey(UPDATED_LANGKEY); + managedUserVM.setCreatedBy(updatedUser.getCreatedBy()); + managedUserVM.setCreatedDate(updatedUser.getCreatedDate()); + managedUserVM.setLastModifiedBy(updatedUser.getLastModifiedBy()); + managedUserVM.setLastModifiedDate(updatedUser.getLastModifiedDate()); + managedUserVM.setAuthorities(Collections.singleton(AuthoritiesConstants.USER)); + + restUserMockMvc + .perform( + put("/api/admin/users").contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(managedUserVM)) + ) + .andExpect(status().isOk()); + + // Validate the User in the database + assertPersistedUsers(users -> { + assertThat(users).hasSize(databaseSizeBeforeUpdate); + User testUser = users.stream().filter(usr -> usr.getId().equals(updatedUser.getId())).findFirst().get(); + assertThat(testUser.getLogin()).isEqualTo(UPDATED_LOGIN); + assertThat(testUser.getFirstName()).isEqualTo(UPDATED_FIRSTNAME); + assertThat(testUser.getLastName()).isEqualTo(UPDATED_LASTNAME); + assertThat(testUser.getEmail()).isEqualTo(UPDATED_EMAIL); + assertThat(testUser.getImageUrl()).isEqualTo(UPDATED_IMAGEURL); + assertThat(testUser.getLangKey()).isEqualTo(UPDATED_LANGKEY); + }); + + userSearchRepository.deleteAll(); + } + + @Test + @Transactional + void updateUserExistingEmail() throws Exception { + // Initialize the database with 2 users + userRepository.saveAndFlush(user); + userSearchRepository.save(user); + + User anotherUser = new User(); + anotherUser.setLogin("jhipster"); + anotherUser.setPassword(RandomStringUtils.randomAlphanumeric(60)); + anotherUser.setActivated(true); + anotherUser.setEmail("jhipster@localhost"); + anotherUser.setFirstName("java"); + anotherUser.setLastName("hipster"); + anotherUser.setImageUrl(""); + anotherUser.setLangKey("en"); + userRepository.saveAndFlush(anotherUser); + userSearchRepository.save(anotherUser); + + // Update the user + User updatedUser = userRepository.findById(user.getId()).get(); + + ManagedUserVM managedUserVM = new ManagedUserVM(); + managedUserVM.setId(updatedUser.getId()); + managedUserVM.setLogin(updatedUser.getLogin()); + managedUserVM.setPassword(updatedUser.getPassword()); + managedUserVM.setFirstName(updatedUser.getFirstName()); + managedUserVM.setLastName(updatedUser.getLastName()); + managedUserVM.setEmail("jhipster@localhost"); // this email should already be used by anotherUser + managedUserVM.setActivated(updatedUser.isActivated()); + managedUserVM.setImageUrl(updatedUser.getImageUrl()); + managedUserVM.setLangKey(updatedUser.getLangKey()); + managedUserVM.setCreatedBy(updatedUser.getCreatedBy()); + managedUserVM.setCreatedDate(updatedUser.getCreatedDate()); + managedUserVM.setLastModifiedBy(updatedUser.getLastModifiedBy()); + managedUserVM.setLastModifiedDate(updatedUser.getLastModifiedDate()); + managedUserVM.setAuthorities(Collections.singleton(AuthoritiesConstants.USER)); + + restUserMockMvc + .perform( + put("/api/admin/users").contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(managedUserVM)) + ) + .andExpect(status().isBadRequest()); + + userSearchRepository.deleteAll(); + } + + @Test + @Transactional + void updateUserExistingLogin() throws Exception { + // Initialize the database + userRepository.saveAndFlush(user); + userSearchRepository.save(user); + + User anotherUser = new User(); + anotherUser.setLogin("jhipster"); + anotherUser.setPassword(RandomStringUtils.randomAlphanumeric(60)); + anotherUser.setActivated(true); + anotherUser.setEmail("jhipster@localhost"); + anotherUser.setFirstName("java"); + anotherUser.setLastName("hipster"); + anotherUser.setImageUrl(""); + anotherUser.setLangKey("en"); + userRepository.saveAndFlush(anotherUser); + userSearchRepository.save(anotherUser); + + // Update the user + User updatedUser = userRepository.findById(user.getId()).get(); + + ManagedUserVM managedUserVM = new ManagedUserVM(); + managedUserVM.setId(updatedUser.getId()); + managedUserVM.setLogin("jhipster"); // this login should already be used by anotherUser + managedUserVM.setPassword(updatedUser.getPassword()); + managedUserVM.setFirstName(updatedUser.getFirstName()); + managedUserVM.setLastName(updatedUser.getLastName()); + managedUserVM.setEmail(updatedUser.getEmail()); + managedUserVM.setActivated(updatedUser.isActivated()); + managedUserVM.setImageUrl(updatedUser.getImageUrl()); + managedUserVM.setLangKey(updatedUser.getLangKey()); + managedUserVM.setCreatedBy(updatedUser.getCreatedBy()); + managedUserVM.setCreatedDate(updatedUser.getCreatedDate()); + managedUserVM.setLastModifiedBy(updatedUser.getLastModifiedBy()); + managedUserVM.setLastModifiedDate(updatedUser.getLastModifiedDate()); + managedUserVM.setAuthorities(Collections.singleton(AuthoritiesConstants.USER)); + + restUserMockMvc + .perform( + put("/api/admin/users").contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(managedUserVM)) + ) + .andExpect(status().isBadRequest()); + + userSearchRepository.deleteAll(); + } + + @Test + @Transactional + void deleteUser() throws Exception { + // Initialize the database + userRepository.saveAndFlush(user); + int databaseSizeBeforeDelete = userRepository.findAll().size(); + + // Delete the user + restUserMockMvc + .perform(delete("/api/admin/users/{login}", user.getLogin()).accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isNoContent()); + + assertThat(cacheManager.getCache(UserRepository.USERS_BY_LOGIN_CACHE).get(user.getLogin())).isNull(); + + // Validate the database is empty + assertPersistedUsers(users -> assertThat(users).hasSize(databaseSizeBeforeDelete - 1)); + } + + @Test + void testUserEquals() throws Exception { + TestUtil.equalsVerifier(User.class); + User user1 = new User(); + user1.setId(DEFAULT_ID); + User user2 = new User(); + user2.setId(user1.getId()); + assertThat(user1).isEqualTo(user2); + user2.setId(2L); + assertThat(user1).isNotEqualTo(user2); + user1.setId(null); + assertThat(user1).isNotEqualTo(user2); + } + + @Test + void testUserDTOtoUser() { + AdminUserDTO userDTO = new AdminUserDTO(); + userDTO.setId(DEFAULT_ID); + userDTO.setLogin(DEFAULT_LOGIN); + userDTO.setFirstName(DEFAULT_FIRSTNAME); + userDTO.setLastName(DEFAULT_LASTNAME); + userDTO.setEmail(DEFAULT_EMAIL); + userDTO.setActivated(true); + userDTO.setImageUrl(DEFAULT_IMAGEURL); + userDTO.setLangKey(DEFAULT_LANGKEY); + userDTO.setCreatedBy(DEFAULT_LOGIN); + userDTO.setLastModifiedBy(DEFAULT_LOGIN); + userDTO.setAuthorities(Collections.singleton(AuthoritiesConstants.USER)); + + User user = userMapper.userDTOToUser(userDTO); + assertThat(user.getId()).isEqualTo(DEFAULT_ID); + assertThat(user.getLogin()).isEqualTo(DEFAULT_LOGIN); + assertThat(user.getFirstName()).isEqualTo(DEFAULT_FIRSTNAME); + assertThat(user.getLastName()).isEqualTo(DEFAULT_LASTNAME); + assertThat(user.getEmail()).isEqualTo(DEFAULT_EMAIL); + assertThat(user.isActivated()).isTrue(); + assertThat(user.getImageUrl()).isEqualTo(DEFAULT_IMAGEURL); + assertThat(user.getLangKey()).isEqualTo(DEFAULT_LANGKEY); + assertThat(user.getCreatedBy()).isNull(); + assertThat(user.getCreatedDate()).isNotNull(); + assertThat(user.getLastModifiedBy()).isNull(); + assertThat(user.getLastModifiedDate()).isNotNull(); + assertThat(user.getAuthorities()).extracting("name").containsExactly(AuthoritiesConstants.USER); + } + + @Test + void testUserToUserDTO() { + user.setId(DEFAULT_ID); + user.setCreatedBy(DEFAULT_LOGIN); + user.setCreatedDate(Instant.now()); + user.setLastModifiedBy(DEFAULT_LOGIN); + user.setLastModifiedDate(Instant.now()); + Set authorities = new HashSet<>(); + Authority authority = new Authority(); + authority.setName(AuthoritiesConstants.USER); + authorities.add(authority); + user.setAuthorities(authorities); + + AdminUserDTO userDTO = userMapper.userToAdminUserDTO(user); + + assertThat(userDTO.getId()).isEqualTo(DEFAULT_ID); + assertThat(userDTO.getLogin()).isEqualTo(DEFAULT_LOGIN); + assertThat(userDTO.getFirstName()).isEqualTo(DEFAULT_FIRSTNAME); + assertThat(userDTO.getLastName()).isEqualTo(DEFAULT_LASTNAME); + assertThat(userDTO.getEmail()).isEqualTo(DEFAULT_EMAIL); + assertThat(userDTO.isActivated()).isTrue(); + assertThat(userDTO.getImageUrl()).isEqualTo(DEFAULT_IMAGEURL); + assertThat(userDTO.getLangKey()).isEqualTo(DEFAULT_LANGKEY); + assertThat(userDTO.getCreatedBy()).isEqualTo(DEFAULT_LOGIN); + assertThat(userDTO.getCreatedDate()).isEqualTo(user.getCreatedDate()); + assertThat(userDTO.getLastModifiedBy()).isEqualTo(DEFAULT_LOGIN); + assertThat(userDTO.getLastModifiedDate()).isEqualTo(user.getLastModifiedDate()); + assertThat(userDTO.getAuthorities()).containsExactly(AuthoritiesConstants.USER); + assertThat(userDTO.toString()).isNotNull(); + } + + @Test + void testAuthorityEquals() { + Authority authorityA = new Authority(); + assertThat(authorityA).isNotEqualTo(null).isNotEqualTo(new Object()); + assertThat(authorityA.hashCode()).isZero(); + assertThat(authorityA.toString()).isNotNull(); + + Authority authorityB = new Authority(); + assertThat(authorityA).isEqualTo(authorityB); + + authorityB.setName(AuthoritiesConstants.ADMIN); + assertThat(authorityA).isNotEqualTo(authorityB); + + authorityA.setName(AuthoritiesConstants.USER); + assertThat(authorityA).isNotEqualTo(authorityB); + + authorityB.setName(AuthoritiesConstants.USER); + assertThat(authorityA).isEqualTo(authorityB).hasSameHashCodeAs(authorityB); + } + + private void assertPersistedUsers(Consumer> userAssertion) { + userAssertion.accept(userRepository.findAll()); + } +} diff --git a/src/test/java/org/jhipster/health/web/rest/WeightResourceIT.java b/src/test/java/org/jhipster/health/web/rest/WeightResourceIT.java new file mode 100644 index 00000000..19bfbc1e --- /dev/null +++ b/src/test/java/org/jhipster/health/web/rest/WeightResourceIT.java @@ -0,0 +1,529 @@ +package org.jhipster.health.web.rest; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; +import static org.hamcrest.Matchers.hasItem; +import static org.jhipster.health.web.rest.TestUtil.sameInstant; +import static org.mockito.Mockito.*; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Random; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; +import javax.persistence.EntityManager; +import org.apache.commons.collections4.IterableUtils; +import org.assertj.core.util.IterableUtil; +import org.jhipster.health.IntegrationTest; +import org.jhipster.health.domain.Weight; +import org.jhipster.health.repository.WeightRepository; +import org.jhipster.health.repository.search.WeightSearchRepository; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.http.MediaType; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.transaction.annotation.Transactional; + +/** + * Integration tests for the {@link WeightResource} REST controller. + */ +@IntegrationTest +@ExtendWith(MockitoExtension.class) +@AutoConfigureMockMvc +@WithMockUser +class WeightResourceIT { + + private static final ZonedDateTime DEFAULT_TIMESTAMP = ZonedDateTime.ofInstant(Instant.ofEpochMilli(0L), ZoneOffset.UTC); + private static final ZonedDateTime UPDATED_TIMESTAMP = ZonedDateTime.now(ZoneId.systemDefault()).withNano(0); + + private static final Double DEFAULT_WEIGHT = 1D; + private static final Double UPDATED_WEIGHT = 2D; + + private static final String ENTITY_API_URL = "/api/weights"; + private static final String ENTITY_API_URL_ID = ENTITY_API_URL + "/{id}"; + private static final String ENTITY_SEARCH_API_URL = "/api/_search/weights"; + + private static Random random = new Random(); + private static AtomicLong count = new AtomicLong(random.nextInt() + (2 * Integer.MAX_VALUE)); + + @Autowired + private WeightRepository weightRepository; + + @Mock + private WeightRepository weightRepositoryMock; + + @Autowired + private WeightSearchRepository weightSearchRepository; + + @Autowired + private EntityManager em; + + @Autowired + private MockMvc restWeightMockMvc; + + private Weight weight; + + /** + * Create an entity for this test. + * + * This is a static method, as tests for other entities might also need it, + * if they test an entity which requires the current entity. + */ + public static Weight createEntity(EntityManager em) { + Weight weight = new Weight().timestamp(DEFAULT_TIMESTAMP).weight(DEFAULT_WEIGHT); + return weight; + } + + /** + * Create an updated entity for this test. + * + * This is a static method, as tests for other entities might also need it, + * if they test an entity which requires the current entity. + */ + public static Weight createUpdatedEntity(EntityManager em) { + Weight weight = new Weight().timestamp(UPDATED_TIMESTAMP).weight(UPDATED_WEIGHT); + return weight; + } + + @AfterEach + public void cleanupElasticSearchRepository() { + weightSearchRepository.deleteAll(); + assertThat(weightSearchRepository.count()).isEqualTo(0); + } + + @BeforeEach + public void initTest() { + weight = createEntity(em); + } + + @Test + @Transactional + void createWeight() throws Exception { + int databaseSizeBeforeCreate = weightRepository.findAll().size(); + int searchDatabaseSizeBefore = IterableUtil.sizeOf(weightSearchRepository.findAll()); + // Create the Weight + restWeightMockMvc + .perform(post(ENTITY_API_URL).contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(weight))) + .andExpect(status().isCreated()); + + // Validate the Weight in the database + List weightList = weightRepository.findAll(); + assertThat(weightList).hasSize(databaseSizeBeforeCreate + 1); + await() + .atMost(5, TimeUnit.SECONDS) + .untilAsserted(() -> { + int searchDatabaseSizeAfter = IterableUtil.sizeOf(weightSearchRepository.findAll()); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore + 1); + }); + Weight testWeight = weightList.get(weightList.size() - 1); + assertThat(testWeight.getTimestamp()).isEqualTo(DEFAULT_TIMESTAMP); + assertThat(testWeight.getWeight()).isEqualTo(DEFAULT_WEIGHT); + } + + @Test + @Transactional + void createWeightWithExistingId() throws Exception { + // Create the Weight with an existing ID + weight.setId(1L); + + int databaseSizeBeforeCreate = weightRepository.findAll().size(); + int searchDatabaseSizeBefore = IterableUtil.sizeOf(weightSearchRepository.findAll()); + + // An entity with an existing ID cannot be created, so this API call must fail + restWeightMockMvc + .perform(post(ENTITY_API_URL).contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(weight))) + .andExpect(status().isBadRequest()); + + // Validate the Weight in the database + List weightList = weightRepository.findAll(); + assertThat(weightList).hasSize(databaseSizeBeforeCreate); + int searchDatabaseSizeAfter = IterableUtil.sizeOf(weightSearchRepository.findAll()); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + } + + @Test + @Transactional + void checkTimestampIsRequired() throws Exception { + int databaseSizeBeforeTest = weightRepository.findAll().size(); + int searchDatabaseSizeBefore = IterableUtil.sizeOf(weightSearchRepository.findAll()); + // set the field null + weight.setTimestamp(null); + + // Create the Weight, which fails. + + restWeightMockMvc + .perform(post(ENTITY_API_URL).contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(weight))) + .andExpect(status().isBadRequest()); + + List weightList = weightRepository.findAll(); + assertThat(weightList).hasSize(databaseSizeBeforeTest); + int searchDatabaseSizeAfter = IterableUtil.sizeOf(weightSearchRepository.findAll()); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + } + + @Test + @Transactional + void checkWeightIsRequired() throws Exception { + int databaseSizeBeforeTest = weightRepository.findAll().size(); + int searchDatabaseSizeBefore = IterableUtil.sizeOf(weightSearchRepository.findAll()); + // set the field null + weight.setWeight(null); + + // Create the Weight, which fails. + + restWeightMockMvc + .perform(post(ENTITY_API_URL).contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(weight))) + .andExpect(status().isBadRequest()); + + List weightList = weightRepository.findAll(); + assertThat(weightList).hasSize(databaseSizeBeforeTest); + int searchDatabaseSizeAfter = IterableUtil.sizeOf(weightSearchRepository.findAll()); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + } + + @Test + @Transactional + void getAllWeights() throws Exception { + // Initialize the database + weightRepository.saveAndFlush(weight); + + // Get all the weightList + restWeightMockMvc + .perform(get(ENTITY_API_URL + "?sort=id,desc")) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(jsonPath("$.[*].id").value(hasItem(weight.getId().intValue()))) + .andExpect(jsonPath("$.[*].timestamp").value(hasItem(sameInstant(DEFAULT_TIMESTAMP)))) + .andExpect(jsonPath("$.[*].weight").value(hasItem(DEFAULT_WEIGHT.doubleValue()))); + } + + @SuppressWarnings({ "unchecked" }) + void getAllWeightsWithEagerRelationshipsIsEnabled() throws Exception { + when(weightRepositoryMock.findAllWithEagerRelationships(any())).thenReturn(new PageImpl(new ArrayList<>())); + + restWeightMockMvc.perform(get(ENTITY_API_URL + "?eagerload=true")).andExpect(status().isOk()); + + verify(weightRepositoryMock, times(1)).findAllWithEagerRelationships(any()); + } + + @SuppressWarnings({ "unchecked" }) + void getAllWeightsWithEagerRelationshipsIsNotEnabled() throws Exception { + when(weightRepositoryMock.findAllWithEagerRelationships(any())).thenReturn(new PageImpl(new ArrayList<>())); + + restWeightMockMvc.perform(get(ENTITY_API_URL + "?eagerload=false")).andExpect(status().isOk()); + verify(weightRepositoryMock, times(1)).findAll(any(Pageable.class)); + } + + @Test + @Transactional + void getWeight() throws Exception { + // Initialize the database + weightRepository.saveAndFlush(weight); + + // Get the weight + restWeightMockMvc + .perform(get(ENTITY_API_URL_ID, weight.getId())) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(jsonPath("$.id").value(weight.getId().intValue())) + .andExpect(jsonPath("$.timestamp").value(sameInstant(DEFAULT_TIMESTAMP))) + .andExpect(jsonPath("$.weight").value(DEFAULT_WEIGHT.doubleValue())); + } + + @Test + @Transactional + void getNonExistingWeight() throws Exception { + // Get the weight + restWeightMockMvc.perform(get(ENTITY_API_URL_ID, Long.MAX_VALUE)).andExpect(status().isNotFound()); + } + + @Test + @Transactional + void putExistingWeight() throws Exception { + // Initialize the database + weightRepository.saveAndFlush(weight); + + int databaseSizeBeforeUpdate = weightRepository.findAll().size(); + weightSearchRepository.save(weight); + int searchDatabaseSizeBefore = IterableUtil.sizeOf(weightSearchRepository.findAll()); + + // Update the weight + Weight updatedWeight = weightRepository.findById(weight.getId()).get(); + // Disconnect from session so that the updates on updatedWeight are not directly saved in db + em.detach(updatedWeight); + updatedWeight.timestamp(UPDATED_TIMESTAMP).weight(UPDATED_WEIGHT); + + restWeightMockMvc + .perform( + put(ENTITY_API_URL_ID, updatedWeight.getId()) + .contentType(MediaType.APPLICATION_JSON) + .content(TestUtil.convertObjectToJsonBytes(updatedWeight)) + ) + .andExpect(status().isOk()); + + // Validate the Weight in the database + List weightList = weightRepository.findAll(); + assertThat(weightList).hasSize(databaseSizeBeforeUpdate); + Weight testWeight = weightList.get(weightList.size() - 1); + assertThat(testWeight.getTimestamp()).isEqualTo(UPDATED_TIMESTAMP); + assertThat(testWeight.getWeight()).isEqualTo(UPDATED_WEIGHT); + await() + .atMost(5, TimeUnit.SECONDS) + .untilAsserted(() -> { + int searchDatabaseSizeAfter = IterableUtil.sizeOf(weightSearchRepository.findAll()); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + List weightSearchList = IterableUtils.toList(weightSearchRepository.findAll()); + Weight testWeightSearch = weightSearchList.get(searchDatabaseSizeAfter - 1); + assertThat(testWeightSearch.getTimestamp()).isEqualTo(UPDATED_TIMESTAMP); + assertThat(testWeightSearch.getWeight()).isEqualTo(UPDATED_WEIGHT); + }); + } + + @Test + @Transactional + void putNonExistingWeight() throws Exception { + int databaseSizeBeforeUpdate = weightRepository.findAll().size(); + int searchDatabaseSizeBefore = IterableUtil.sizeOf(weightSearchRepository.findAll()); + weight.setId(count.incrementAndGet()); + + // If the entity doesn't have an ID, it will throw BadRequestAlertException + restWeightMockMvc + .perform( + put(ENTITY_API_URL_ID, weight.getId()) + .contentType(MediaType.APPLICATION_JSON) + .content(TestUtil.convertObjectToJsonBytes(weight)) + ) + .andExpect(status().isBadRequest()); + + // Validate the Weight in the database + List weightList = weightRepository.findAll(); + assertThat(weightList).hasSize(databaseSizeBeforeUpdate); + int searchDatabaseSizeAfter = IterableUtil.sizeOf(weightSearchRepository.findAll()); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + } + + @Test + @Transactional + void putWithIdMismatchWeight() throws Exception { + int databaseSizeBeforeUpdate = weightRepository.findAll().size(); + int searchDatabaseSizeBefore = IterableUtil.sizeOf(weightSearchRepository.findAll()); + weight.setId(count.incrementAndGet()); + + // If url ID doesn't match entity ID, it will throw BadRequestAlertException + restWeightMockMvc + .perform( + put(ENTITY_API_URL_ID, count.incrementAndGet()) + .contentType(MediaType.APPLICATION_JSON) + .content(TestUtil.convertObjectToJsonBytes(weight)) + ) + .andExpect(status().isBadRequest()); + + // Validate the Weight in the database + List weightList = weightRepository.findAll(); + assertThat(weightList).hasSize(databaseSizeBeforeUpdate); + int searchDatabaseSizeAfter = IterableUtil.sizeOf(weightSearchRepository.findAll()); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + } + + @Test + @Transactional + void putWithMissingIdPathParamWeight() throws Exception { + int databaseSizeBeforeUpdate = weightRepository.findAll().size(); + int searchDatabaseSizeBefore = IterableUtil.sizeOf(weightSearchRepository.findAll()); + weight.setId(count.incrementAndGet()); + + // If url ID doesn't match entity ID, it will throw BadRequestAlertException + restWeightMockMvc + .perform(put(ENTITY_API_URL).contentType(MediaType.APPLICATION_JSON).content(TestUtil.convertObjectToJsonBytes(weight))) + .andExpect(status().isMethodNotAllowed()); + + // Validate the Weight in the database + List weightList = weightRepository.findAll(); + assertThat(weightList).hasSize(databaseSizeBeforeUpdate); + int searchDatabaseSizeAfter = IterableUtil.sizeOf(weightSearchRepository.findAll()); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + } + + @Test + @Transactional + void partialUpdateWeightWithPatch() throws Exception { + // Initialize the database + weightRepository.saveAndFlush(weight); + + int databaseSizeBeforeUpdate = weightRepository.findAll().size(); + + // Update the weight using partial update + Weight partialUpdatedWeight = new Weight(); + partialUpdatedWeight.setId(weight.getId()); + + partialUpdatedWeight.weight(UPDATED_WEIGHT); + + restWeightMockMvc + .perform( + patch(ENTITY_API_URL_ID, partialUpdatedWeight.getId()) + .contentType("application/merge-patch+json") + .content(TestUtil.convertObjectToJsonBytes(partialUpdatedWeight)) + ) + .andExpect(status().isOk()); + + // Validate the Weight in the database + List weightList = weightRepository.findAll(); + assertThat(weightList).hasSize(databaseSizeBeforeUpdate); + Weight testWeight = weightList.get(weightList.size() - 1); + assertThat(testWeight.getTimestamp()).isEqualTo(DEFAULT_TIMESTAMP); + assertThat(testWeight.getWeight()).isEqualTo(UPDATED_WEIGHT); + } + + @Test + @Transactional + void fullUpdateWeightWithPatch() throws Exception { + // Initialize the database + weightRepository.saveAndFlush(weight); + + int databaseSizeBeforeUpdate = weightRepository.findAll().size(); + + // Update the weight using partial update + Weight partialUpdatedWeight = new Weight(); + partialUpdatedWeight.setId(weight.getId()); + + partialUpdatedWeight.timestamp(UPDATED_TIMESTAMP).weight(UPDATED_WEIGHT); + + restWeightMockMvc + .perform( + patch(ENTITY_API_URL_ID, partialUpdatedWeight.getId()) + .contentType("application/merge-patch+json") + .content(TestUtil.convertObjectToJsonBytes(partialUpdatedWeight)) + ) + .andExpect(status().isOk()); + + // Validate the Weight in the database + List weightList = weightRepository.findAll(); + assertThat(weightList).hasSize(databaseSizeBeforeUpdate); + Weight testWeight = weightList.get(weightList.size() - 1); + assertThat(testWeight.getTimestamp()).isEqualTo(UPDATED_TIMESTAMP); + assertThat(testWeight.getWeight()).isEqualTo(UPDATED_WEIGHT); + } + + @Test + @Transactional + void patchNonExistingWeight() throws Exception { + int databaseSizeBeforeUpdate = weightRepository.findAll().size(); + int searchDatabaseSizeBefore = IterableUtil.sizeOf(weightSearchRepository.findAll()); + weight.setId(count.incrementAndGet()); + + // If the entity doesn't have an ID, it will throw BadRequestAlertException + restWeightMockMvc + .perform( + patch(ENTITY_API_URL_ID, weight.getId()) + .contentType("application/merge-patch+json") + .content(TestUtil.convertObjectToJsonBytes(weight)) + ) + .andExpect(status().isBadRequest()); + + // Validate the Weight in the database + List weightList = weightRepository.findAll(); + assertThat(weightList).hasSize(databaseSizeBeforeUpdate); + int searchDatabaseSizeAfter = IterableUtil.sizeOf(weightSearchRepository.findAll()); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + } + + @Test + @Transactional + void patchWithIdMismatchWeight() throws Exception { + int databaseSizeBeforeUpdate = weightRepository.findAll().size(); + int searchDatabaseSizeBefore = IterableUtil.sizeOf(weightSearchRepository.findAll()); + weight.setId(count.incrementAndGet()); + + // If url ID doesn't match entity ID, it will throw BadRequestAlertException + restWeightMockMvc + .perform( + patch(ENTITY_API_URL_ID, count.incrementAndGet()) + .contentType("application/merge-patch+json") + .content(TestUtil.convertObjectToJsonBytes(weight)) + ) + .andExpect(status().isBadRequest()); + + // Validate the Weight in the database + List weightList = weightRepository.findAll(); + assertThat(weightList).hasSize(databaseSizeBeforeUpdate); + int searchDatabaseSizeAfter = IterableUtil.sizeOf(weightSearchRepository.findAll()); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + } + + @Test + @Transactional + void patchWithMissingIdPathParamWeight() throws Exception { + int databaseSizeBeforeUpdate = weightRepository.findAll().size(); + int searchDatabaseSizeBefore = IterableUtil.sizeOf(weightSearchRepository.findAll()); + weight.setId(count.incrementAndGet()); + + // If url ID doesn't match entity ID, it will throw BadRequestAlertException + restWeightMockMvc + .perform(patch(ENTITY_API_URL).contentType("application/merge-patch+json").content(TestUtil.convertObjectToJsonBytes(weight))) + .andExpect(status().isMethodNotAllowed()); + + // Validate the Weight in the database + List weightList = weightRepository.findAll(); + assertThat(weightList).hasSize(databaseSizeBeforeUpdate); + int searchDatabaseSizeAfter = IterableUtil.sizeOf(weightSearchRepository.findAll()); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore); + } + + @Test + @Transactional + void deleteWeight() throws Exception { + // Initialize the database + weightRepository.saveAndFlush(weight); + weightRepository.save(weight); + weightSearchRepository.save(weight); + + int databaseSizeBeforeDelete = weightRepository.findAll().size(); + int searchDatabaseSizeBefore = IterableUtil.sizeOf(weightSearchRepository.findAll()); + assertThat(searchDatabaseSizeBefore).isEqualTo(databaseSizeBeforeDelete); + + // Delete the weight + restWeightMockMvc + .perform(delete(ENTITY_API_URL_ID, weight.getId()).accept(MediaType.APPLICATION_JSON)) + .andExpect(status().isNoContent()); + + // Validate the database contains one less item + List weightList = weightRepository.findAll(); + assertThat(weightList).hasSize(databaseSizeBeforeDelete - 1); + int searchDatabaseSizeAfter = IterableUtil.sizeOf(weightSearchRepository.findAll()); + assertThat(searchDatabaseSizeAfter).isEqualTo(searchDatabaseSizeBefore - 1); + } + + @Test + @Transactional + void searchWeight() throws Exception { + // Initialize the database + weight = weightRepository.saveAndFlush(weight); + weightSearchRepository.save(weight); + + // Search the weight + restWeightMockMvc + .perform(get(ENTITY_SEARCH_API_URL + "?query=id:" + weight.getId())) + .andExpect(status().isOk()) + .andExpect(content().contentType(MediaType.APPLICATION_JSON_VALUE)) + .andExpect(jsonPath("$.[*].id").value(hasItem(weight.getId().intValue()))) + .andExpect(jsonPath("$.[*].timestamp").value(hasItem(sameInstant(DEFAULT_TIMESTAMP)))) + .andExpect(jsonPath("$.[*].weight").value(hasItem(DEFAULT_WEIGHT.doubleValue()))); + } +} diff --git a/src/test/java/org/jhipster/health/web/rest/WithUnauthenticatedMockUser.java b/src/test/java/org/jhipster/health/web/rest/WithUnauthenticatedMockUser.java new file mode 100644 index 00000000..737434ca --- /dev/null +++ b/src/test/java/org/jhipster/health/web/rest/WithUnauthenticatedMockUser.java @@ -0,0 +1,23 @@ +package org.jhipster.health.web.rest; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.test.context.support.WithSecurityContext; +import org.springframework.security.test.context.support.WithSecurityContextFactory; + +@Target({ ElementType.METHOD, ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@WithSecurityContext(factory = WithUnauthenticatedMockUser.Factory.class) +public @interface WithUnauthenticatedMockUser { + class Factory implements WithSecurityContextFactory { + + @Override + public SecurityContext createSecurityContext(WithUnauthenticatedMockUser annotation) { + return SecurityContextHolder.createEmptyContext(); + } + } +} diff --git a/src/test/java/org/jhipster/health/web/rest/errors/ExceptionTranslatorIT.java b/src/test/java/org/jhipster/health/web/rest/errors/ExceptionTranslatorIT.java new file mode 100644 index 00000000..8a9803f0 --- /dev/null +++ b/src/test/java/org/jhipster/health/web/rest/errors/ExceptionTranslatorIT.java @@ -0,0 +1,118 @@ +package org.jhipster.health.web.rest.errors; + +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +import org.jhipster.health.IntegrationTest; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.http.MediaType; +import org.springframework.security.test.context.support.WithMockUser; +import org.springframework.test.web.servlet.MockMvc; + +/** + * Integration tests {@link ExceptionTranslator} controller advice. + */ +@WithMockUser +@AutoConfigureMockMvc +@IntegrationTest +class ExceptionTranslatorIT { + + @Autowired + private MockMvc mockMvc; + + @Test + void testConcurrencyFailure() throws Exception { + mockMvc + .perform(get("/api/exception-translator-test/concurrency-failure")) + .andExpect(status().isConflict()) + .andExpect(content().contentType(MediaType.APPLICATION_PROBLEM_JSON)) + .andExpect(jsonPath("$.message").value(ErrorConstants.ERR_CONCURRENCY_FAILURE)); + } + + @Test + void testMethodArgumentNotValid() throws Exception { + mockMvc + .perform(post("/api/exception-translator-test/method-argument").content("{}").contentType(MediaType.APPLICATION_JSON)) + .andExpect(status().isBadRequest()) + .andExpect(content().contentType(MediaType.APPLICATION_PROBLEM_JSON)) + .andExpect(jsonPath("$.message").value(ErrorConstants.ERR_VALIDATION)) + .andExpect(jsonPath("$.fieldErrors.[0].objectName").value("test")) + .andExpect(jsonPath("$.fieldErrors.[0].field").value("test")) + .andExpect(jsonPath("$.fieldErrors.[0].message").value("must not be null")); + } + + @Test + void testMissingServletRequestPartException() throws Exception { + mockMvc + .perform(get("/api/exception-translator-test/missing-servlet-request-part")) + .andExpect(status().isBadRequest()) + .andExpect(content().contentType(MediaType.APPLICATION_PROBLEM_JSON)) + .andExpect(jsonPath("$.message").value("error.http.400")); + } + + @Test + void testMissingServletRequestParameterException() throws Exception { + mockMvc + .perform(get("/api/exception-translator-test/missing-servlet-request-parameter")) + .andExpect(status().isBadRequest()) + .andExpect(content().contentType(MediaType.APPLICATION_PROBLEM_JSON)) + .andExpect(jsonPath("$.message").value("error.http.400")); + } + + @Test + void testAccessDenied() throws Exception { + mockMvc + .perform(get("/api/exception-translator-test/access-denied")) + .andExpect(status().isForbidden()) + .andExpect(content().contentType(MediaType.APPLICATION_PROBLEM_JSON)) + .andExpect(jsonPath("$.message").value("error.http.403")) + .andExpect(jsonPath("$.detail").value("test access denied!")); + } + + @Test + void testUnauthorized() throws Exception { + mockMvc + .perform(get("/api/exception-translator-test/unauthorized")) + .andExpect(status().isUnauthorized()) + .andExpect(content().contentType(MediaType.APPLICATION_PROBLEM_JSON)) + .andExpect(jsonPath("$.message").value("error.http.401")) + .andExpect(jsonPath("$.path").value("/api/exception-translator-test/unauthorized")) + .andExpect(jsonPath("$.detail").value("test authentication failed!")); + } + + @Test + void testMethodNotSupported() throws Exception { + mockMvc + .perform(post("/api/exception-translator-test/access-denied")) + .andExpect(status().isMethodNotAllowed()) + .andExpect(content().contentType(MediaType.APPLICATION_PROBLEM_JSON)) + .andExpect(jsonPath("$.message").value("error.http.405")) + .andExpect(jsonPath("$.detail").value("Request method 'POST' not supported")); + } + + @Test + void testExceptionWithResponseStatus() throws Exception { + mockMvc + .perform(get("/api/exception-translator-test/response-status")) + .andExpect(status().isBadRequest()) + .andExpect(content().contentType(MediaType.APPLICATION_PROBLEM_JSON)) + .andExpect(jsonPath("$.message").value("error.http.400")) + .andExpect(jsonPath("$.title").value("test response status")); + } + + @Test + void testInternalServerError() throws Exception { + mockMvc + .perform(get("/api/exception-translator-test/internal-server-error")) + .andExpect(status().isInternalServerError()) + .andExpect(content().contentType(MediaType.APPLICATION_PROBLEM_JSON)) + .andExpect(jsonPath("$.message").value("error.http.500")) + .andExpect(jsonPath("$.title").value("Internal Server Error")); + } +} diff --git a/src/test/java/org/jhipster/health/web/rest/errors/ExceptionTranslatorTestController.java b/src/test/java/org/jhipster/health/web/rest/errors/ExceptionTranslatorTestController.java new file mode 100644 index 00000000..afcc7c7c --- /dev/null +++ b/src/test/java/org/jhipster/health/web/rest/errors/ExceptionTranslatorTestController.java @@ -0,0 +1,66 @@ +package org.jhipster.health.web.rest.errors; + +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import org.springframework.dao.ConcurrencyFailureException; +import org.springframework.http.HttpStatus; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/api/exception-translator-test") +public class ExceptionTranslatorTestController { + + @GetMapping("/concurrency-failure") + public void concurrencyFailure() { + throw new ConcurrencyFailureException("test concurrency failure"); + } + + @PostMapping("/method-argument") + public void methodArgument(@Valid @RequestBody TestDTO testDTO) {} + + @GetMapping("/missing-servlet-request-part") + public void missingServletRequestPartException(@RequestPart String part) {} + + @GetMapping("/missing-servlet-request-parameter") + public void missingServletRequestParameterException(@RequestParam String param) {} + + @GetMapping("/access-denied") + public void accessdenied() { + throw new AccessDeniedException("test access denied!"); + } + + @GetMapping("/unauthorized") + public void unauthorized() { + throw new BadCredentialsException("test authentication failed!"); + } + + @GetMapping("/response-status") + public void exceptionWithResponseStatus() { + throw new TestResponseStatusException(); + } + + @GetMapping("/internal-server-error") + public void internalServerError() { + throw new RuntimeException(); + } + + public static class TestDTO { + + @NotNull + private String test; + + public String getTest() { + return test; + } + + public void setTest(String test) { + this.test = test; + } + } + + @ResponseStatus(value = HttpStatus.BAD_REQUEST, reason = "test response status") + @SuppressWarnings("serial") + public static class TestResponseStatusException extends RuntimeException {} +} diff --git a/src/test/javascript/cypress/.eslintrc.json b/src/test/javascript/cypress/.eslintrc.json new file mode 100644 index 00000000..16012997 --- /dev/null +++ b/src/test/javascript/cypress/.eslintrc.json @@ -0,0 +1,12 @@ +{ + "root": true, + "parser": "@typescript-eslint/parser", + "plugins": ["cypress", "@typescript-eslint"], + "env": { + "cypress/globals": true + }, + "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended", "plugin:cypress/recommended"], + "rules": { + "@typescript-eslint/no-namespace": ["error", { "allowDeclarations": true }] + } +} diff --git a/src/test/javascript/cypress/e2e/account/login-page.cy.ts b/src/test/javascript/cypress/e2e/account/login-page.cy.ts new file mode 100644 index 00000000..578c20b0 --- /dev/null +++ b/src/test/javascript/cypress/e2e/account/login-page.cy.ts @@ -0,0 +1,56 @@ +import { + titleLoginSelector, + errorLoginSelector, + usernameLoginSelector, + passwordLoginSelector, + submitLoginSelector, +} from '../../support/commands'; + +describe('login modal', () => { + const username = Cypress.env('E2E_USERNAME') ?? 'user'; + const password = Cypress.env('E2E_PASSWORD') ?? 'user'; + + beforeEach(() => { + cy.visit(''); + cy.clickOnLoginItem(); + }); + + beforeEach(() => { + cy.intercept('POST', '/api/authenticate').as('authenticate'); + }); + + it('greets with signin', () => { + cy.get(titleLoginSelector).should('be.visible'); + }); + + it('requires username', () => { + cy.get(passwordLoginSelector).type('a-password'); + cy.get(submitLoginSelector).click(); + cy.wait('@authenticate').then(({ response }) => expect(response.statusCode).to.equal(400)); + // login page should stay open when login fails + cy.get(titleLoginSelector).should('be.visible'); + }); + + it('requires password', () => { + cy.get(usernameLoginSelector).type('a-login'); + cy.get(submitLoginSelector).click(); + cy.wait('@authenticate').then(({ response }) => expect(response.statusCode).to.equal(400)); + cy.get(errorLoginSelector).should('be.visible'); + }); + + it('errors when password is incorrect', () => { + cy.get(usernameLoginSelector).type(username); + cy.get(passwordLoginSelector).type('bad-password'); + cy.get(submitLoginSelector).click(); + cy.wait('@authenticate').then(({ response }) => expect(response.statusCode).to.equal(401)); + cy.get(errorLoginSelector).should('be.visible'); + }); + + it('go to login page when successfully logs in', () => { + cy.get(usernameLoginSelector).type(username); + cy.get(passwordLoginSelector).type(password); + cy.get(submitLoginSelector).click(); + cy.wait('@authenticate').then(({ response }) => expect(response.statusCode).to.equal(200)); + cy.hash().should('eq', ''); + }); +}); diff --git a/src/test/javascript/cypress/e2e/account/password-page.cy.ts b/src/test/javascript/cypress/e2e/account/password-page.cy.ts new file mode 100644 index 00000000..ed62edfa --- /dev/null +++ b/src/test/javascript/cypress/e2e/account/password-page.cy.ts @@ -0,0 +1,61 @@ +import { + currentPasswordSelector, + newPasswordSelector, + confirmPasswordSelector, + submitPasswordSelector, + classInvalid, + classValid, +} from '../../support/commands'; + +describe('/account/password', () => { + const username = Cypress.env('E2E_USERNAME') ?? 'user'; + const password = Cypress.env('E2E_PASSWORD') ?? 'user'; + + beforeEach(() => { + cy.login(username, password); + cy.visit('/account/password'); + }); + + beforeEach(() => { + cy.intercept('POST', '/api/account/change-password').as('passwordSave'); + }); + + it('should be accessible through menu', () => { + cy.visit(''); + cy.clickOnPasswordItem(); + cy.url().should('match', /\/account\/password$/); + }); + + it('requires current password', () => { + cy.get(currentPasswordSelector) + .should('have.class', classInvalid) + .type('wrong-current-password') + .blur() + .should('have.class', classValid); + }); + + it('requires new password', () => { + cy.get(newPasswordSelector).should('have.class', classInvalid).type('jhipster').blur().should('have.class', classValid); + }); + + it('requires confirm new password', () => { + cy.get(newPasswordSelector).type('jhipster'); + cy.get(confirmPasswordSelector).should('have.class', classInvalid).type('jhipster').blur().should('have.class', classValid); + }); + + it('should fail to update password when using incorrect current password', () => { + cy.get(currentPasswordSelector).type('wrong-current-password'); + cy.get(newPasswordSelector).type('jhipster'); + cy.get(confirmPasswordSelector).type('jhipster'); + cy.get(submitPasswordSelector).click(); + cy.wait('@passwordSave').then(({ response }) => expect(response.statusCode).to.equal(400)); + }); + + it('should be able to update password', () => { + cy.get(currentPasswordSelector).type(password); + cy.get(newPasswordSelector).type(password); + cy.get(confirmPasswordSelector).type(password); + cy.get(submitPasswordSelector).click(); + cy.wait('@passwordSave').then(({ response }) => expect(response.statusCode).to.equal(200)); + }); +}); diff --git a/src/test/javascript/cypress/e2e/account/register-page.cy.ts b/src/test/javascript/cypress/e2e/account/register-page.cy.ts new file mode 100644 index 00000000..523da650 --- /dev/null +++ b/src/test/javascript/cypress/e2e/account/register-page.cy.ts @@ -0,0 +1,67 @@ +import { + usernameRegisterSelector, + emailRegisterSelector, + firstPasswordRegisterSelector, + secondPasswordRegisterSelector, + submitRegisterSelector, + classInvalid, + classValid, +} from '../../support/commands'; + +describe('/account/register', () => { + beforeEach(() => { + cy.visit('/account/register'); + }); + + beforeEach(() => { + cy.intercept('POST', '/api/register').as('registerSave'); + }); + + it('should be accessible through menu', () => { + cy.visit(''); + cy.clickOnRegisterItem(); + cy.url().should('match', /\/account\/register$/); + }); + + it('should load the register page', () => { + cy.get(submitRegisterSelector).should('be.visible'); + }); + + it('requires username', () => { + cy.get(usernameRegisterSelector).should('have.class', classInvalid).type('test').blur().should('have.class', classValid); + }); + + it('should not accept invalid email', () => { + cy.get(emailRegisterSelector).should('have.class', classInvalid).type('testtest.fr').blur().should('have.class', classInvalid); + }); + + it('requires email in correct format', () => { + cy.get(emailRegisterSelector).should('have.class', classInvalid).type('test@test.fr').blur().should('have.class', classValid); + }); + + it('requires first password', () => { + cy.get(firstPasswordRegisterSelector).should('have.class', classInvalid).type('test@test.fr').blur().should('have.class', classValid); + }); + + it('requires password and confirm password to be same', () => { + cy.get(firstPasswordRegisterSelector).should('have.class', classInvalid).type('test').blur().should('have.class', classValid); + cy.get(secondPasswordRegisterSelector).should('have.class', classInvalid).type('test').blur().should('have.class', classValid); + }); + + it('requires password and confirm password have not the same value', () => { + cy.get(firstPasswordRegisterSelector).should('have.class', classInvalid).type('test').blur().should('have.class', classValid); + cy.get(secondPasswordRegisterSelector).should('have.class', classInvalid).type('otherPassword'); + cy.get(submitRegisterSelector).should('be.disabled'); + }); + + it('register a valid user', () => { + const randomEmail = 'Jennyfer_Parker7@gmail.com'; + const randomUsername = 'Ashleigh10'; + cy.get(usernameRegisterSelector).type(randomUsername); + cy.get(emailRegisterSelector).type(randomEmail); + cy.get(firstPasswordRegisterSelector).type('jondoe'); + cy.get(secondPasswordRegisterSelector).type('jondoe'); + cy.get(submitRegisterSelector).click(); + cy.wait('@registerSave').then(({ response }) => expect(response.statusCode).to.equal(201)); + }); +}); diff --git a/src/test/javascript/cypress/e2e/account/reset-password-page.cy.ts b/src/test/javascript/cypress/e2e/account/reset-password-page.cy.ts new file mode 100644 index 00000000..ee64cb24 --- /dev/null +++ b/src/test/javascript/cypress/e2e/account/reset-password-page.cy.ts @@ -0,0 +1,34 @@ +import { + usernameLoginSelector, + forgetYourPasswordSelector, + emailResetPasswordSelector, + submitInitResetPasswordSelector, + classInvalid, + classValid, +} from '../../support/commands'; + +describe('forgot your password', () => { + const username = Cypress.env('E2E_USERNAME') ?? 'user'; + + beforeEach(() => { + cy.visit(''); + cy.clickOnLoginItem(); + cy.get(usernameLoginSelector).type(username); + cy.get(forgetYourPasswordSelector).click(); + }); + + beforeEach(() => { + cy.intercept('POST', '/api/account/reset-password/init').as('initResetPassword'); + }); + + it('requires email', () => { + cy.get(emailResetPasswordSelector).should('have.class', classInvalid).type('user@gmail.com'); + cy.get(emailResetPasswordSelector).should('have.class', classValid); + }); + + it('should be able to init reset password', () => { + cy.get(emailResetPasswordSelector).type('user@gmail.com'); + cy.get(submitInitResetPasswordSelector).click({ force: true }); + cy.wait('@initResetPassword').then(({ response }) => expect(response.statusCode).to.equal(200)); + }); +}); diff --git a/src/test/javascript/cypress/e2e/account/settings-page.cy.ts b/src/test/javascript/cypress/e2e/account/settings-page.cy.ts new file mode 100644 index 00000000..c41e16f2 --- /dev/null +++ b/src/test/javascript/cypress/e2e/account/settings-page.cy.ts @@ -0,0 +1,66 @@ +import { firstNameSettingsSelector, lastNameSettingsSelector, submitSettingsSelector, emailSettingsSelector } from '../../support/commands'; + +describe('/account/settings', () => { + const adminUsername = Cypress.env('E2E_USERNAME') ?? 'admin'; + const adminPassword = Cypress.env('E2E_PASSWORD') ?? 'admin'; + const username = Cypress.env('E2E_USERNAME') ?? 'user'; + const password = Cypress.env('E2E_PASSWORD') ?? 'user'; + + beforeEach(() => { + cy.login(username, password); + cy.visit('/account/settings'); + }); + + beforeEach(() => { + cy.intercept('POST', '/api/account').as('settingsSave'); + }); + + it('should be accessible through menu', () => { + cy.visit(''); + cy.clickOnSettingsItem(); + cy.url().should('match', /\/account\/settings$/); + }); + + it("should be able to change 'user' firstname settings", () => { + cy.get(firstNameSettingsSelector).clear().type('jhipster'); + // need to modify email because default email does not match regex in vue + cy.get(emailSettingsSelector).clear().type('user@localhost.fr'); + cy.get(submitSettingsSelector).click(); + cy.wait('@settingsSave').then(({ response }) => expect(response.statusCode).to.equal(200)); + }); + + it("should be able to change 'user' lastname settings", () => { + cy.get(lastNameSettingsSelector).clear().type('retspihj'); + // need to modify email because default email does not match regex in vue + cy.get(emailSettingsSelector).clear().type('user@localhost.fr'); + cy.get(submitSettingsSelector).click(); + cy.wait('@settingsSave').then(({ response }) => expect(response.statusCode).to.equal(200)); + }); + + it("should be able to change 'user' email settings", () => { + cy.get(emailSettingsSelector).clear().type('user@localhost.fr'); + cy.get(submitSettingsSelector).click(); + cy.wait('@settingsSave').then(({ response }) => expect(response.statusCode).to.equal(200)); + }); + + describe('if there is another user with an email', () => { + before(() => { + cy.login(adminUsername, adminPassword); + cy.visit('/account/settings'); + cy.get(emailSettingsSelector).clear().type('admin@localhost.fr'); + cy.intercept({ + method: 'POST', + url: '/api/account', + times: 1, + }).as('settingsSave'); + cy.get(submitSettingsSelector).click(); + cy.wait('@settingsSave'); + }); + + it("should not be able to change 'user' email to same value", () => { + cy.get(emailSettingsSelector).clear().type('admin@localhost.fr'); + cy.get(submitSettingsSelector).click(); + cy.wait('@settingsSave').then(({ response }) => expect(response.statusCode).to.equal(400)); + }); + }); +}); diff --git a/src/test/javascript/cypress/e2e/administration/administration.cy.ts b/src/test/javascript/cypress/e2e/administration/administration.cy.ts new file mode 100644 index 00000000..5816d426 --- /dev/null +++ b/src/test/javascript/cypress/e2e/administration/administration.cy.ts @@ -0,0 +1,71 @@ +import { + userManagementPageHeadingSelector, + metricsPageHeadingSelector, + healthPageHeadingSelector, + logsPageHeadingSelector, + configurationPageHeadingSelector, + swaggerPageSelector, + swaggerFrameSelector, +} from '../../support/commands'; + +describe('/admin', () => { + const username = Cypress.env('E2E_USERNAME') ?? 'admin'; + const password = Cypress.env('E2E_PASSWORD') ?? 'admin'; + + beforeEach(() => { + cy.login(username, password); + cy.visit(''); + }); + + describe('/user-management', () => { + it('should load the page', () => { + cy.clickOnAdminMenuItem('user-management'); + cy.get(userManagementPageHeadingSelector).should('be.visible'); + }); + }); + + describe('/metrics', () => { + it('should load the page', () => { + cy.clickOnAdminMenuItem('metrics'); + cy.get(metricsPageHeadingSelector).should('be.visible'); + }); + }); + + describe('/health', () => { + it('should load the page', () => { + cy.clickOnAdminMenuItem('health'); + cy.get(healthPageHeadingSelector).should('be.visible'); + }); + }); + + describe('/logs', () => { + it('should load the page', () => { + cy.clickOnAdminMenuItem('logs'); + cy.get(logsPageHeadingSelector).should('be.visible'); + }); + }); + + describe('/configuration', () => { + it('should load the page', () => { + cy.clickOnAdminMenuItem('configuration'); + cy.get(configurationPageHeadingSelector).should('be.visible'); + }); + }); + + describe('/docs', () => { + it('should load the page', () => { + cy.getManagementInfo().then(info => { + if (info.activeProfiles.includes('api-docs')) { + cy.clickOnAdminMenuItem('docs'); + cy.get(swaggerFrameSelector) + .should('be.visible') + .then(() => { + // Wait iframe to load + cy.wait(500); // eslint-disable-line cypress/no-unnecessary-waiting + cy.get(swaggerFrameSelector).its('0.contentDocument.body').find(swaggerPageSelector, { timeout: 15000 }).should('be.visible'); + }); + } + }); + }); + }); +}); diff --git a/src/test/javascript/cypress/e2e/entity/blood-pressure.cy.ts b/src/test/javascript/cypress/e2e/entity/blood-pressure.cy.ts new file mode 100644 index 00000000..2e1a1e0b --- /dev/null +++ b/src/test/javascript/cypress/e2e/entity/blood-pressure.cy.ts @@ -0,0 +1,181 @@ +import { + entityTableSelector, + entityDetailsButtonSelector, + entityDetailsBackButtonSelector, + entityCreateButtonSelector, + entityCreateSaveButtonSelector, + entityCreateCancelButtonSelector, + entityEditButtonSelector, + entityDeleteButtonSelector, + entityConfirmDeleteButtonSelector, +} from '../../support/entity'; + +describe('BloodPressure e2e test', () => { + const bloodPressurePageUrl = '/blood-pressure'; + const bloodPressurePageUrlPattern = new RegExp('/blood-pressure(\\?.*)?$'); + const username = Cypress.env('E2E_USERNAME') ?? 'user'; + const password = Cypress.env('E2E_PASSWORD') ?? 'user'; + const bloodPressureSample = { timestamp: '2022-11-07T20:18:57.606Z', systolic: 27476, diastolic: 27392 }; + + let bloodPressure; + + beforeEach(() => { + cy.login(username, password); + }); + + beforeEach(() => { + cy.intercept('GET', '/api/blood-pressures+(?*|)').as('entitiesRequest'); + cy.intercept('POST', '/api/blood-pressures').as('postEntityRequest'); + cy.intercept('DELETE', '/api/blood-pressures/*').as('deleteEntityRequest'); + }); + + afterEach(() => { + if (bloodPressure) { + cy.authenticatedRequest({ + method: 'DELETE', + url: `/api/blood-pressures/${bloodPressure.id}`, + }).then(() => { + bloodPressure = undefined; + }); + } + }); + + it('BloodPressures menu should load BloodPressures page', () => { + cy.visit('/'); + cy.clickOnEntityMenuItem('blood-pressure'); + cy.wait('@entitiesRequest').then(({ response }) => { + if (response.body.length === 0) { + cy.get(entityTableSelector).should('not.exist'); + } else { + cy.get(entityTableSelector).should('exist'); + } + }); + cy.getEntityHeading('BloodPressure').should('exist'); + cy.url().should('match', bloodPressurePageUrlPattern); + }); + + describe('BloodPressure page', () => { + describe('create button click', () => { + beforeEach(() => { + cy.visit(bloodPressurePageUrl); + cy.wait('@entitiesRequest'); + }); + + it('should load create BloodPressure page', () => { + cy.get(entityCreateButtonSelector).click(); + cy.url().should('match', new RegExp('/blood-pressure/new$')); + cy.getEntityCreateUpdateHeading('BloodPressure'); + cy.get(entityCreateSaveButtonSelector).should('exist'); + cy.get(entityCreateCancelButtonSelector).click(); + cy.wait('@entitiesRequest').then(({ response }) => { + expect(response.statusCode).to.equal(200); + }); + cy.url().should('match', bloodPressurePageUrlPattern); + }); + }); + + describe('with existing value', () => { + beforeEach(() => { + cy.authenticatedRequest({ + method: 'POST', + url: '/api/blood-pressures', + body: bloodPressureSample, + }).then(({ body }) => { + bloodPressure = body; + + cy.intercept( + { + method: 'GET', + url: '/api/blood-pressures+(?*|)', + times: 1, + }, + { + statusCode: 200, + headers: { + link: '; rel="last",; rel="first"', + }, + body: [bloodPressure], + } + ).as('entitiesRequestInternal'); + }); + + cy.visit(bloodPressurePageUrl); + + cy.wait('@entitiesRequestInternal'); + }); + + it('detail button click should load details BloodPressure page', () => { + cy.get(entityDetailsButtonSelector).first().click(); + cy.getEntityDetailsHeading('bloodPressure'); + cy.get(entityDetailsBackButtonSelector).click(); + cy.wait('@entitiesRequest').then(({ response }) => { + expect(response.statusCode).to.equal(200); + }); + cy.url().should('match', bloodPressurePageUrlPattern); + }); + + it('edit button click should load edit BloodPressure page and go back', () => { + cy.get(entityEditButtonSelector).first().click(); + cy.getEntityCreateUpdateHeading('BloodPressure'); + cy.get(entityCreateSaveButtonSelector).should('exist'); + cy.get(entityCreateCancelButtonSelector).click(); + cy.wait('@entitiesRequest').then(({ response }) => { + expect(response.statusCode).to.equal(200); + }); + cy.url().should('match', bloodPressurePageUrlPattern); + }); + + it('edit button click should load edit BloodPressure page and save', () => { + cy.get(entityEditButtonSelector).first().click(); + cy.getEntityCreateUpdateHeading('BloodPressure'); + cy.get(entityCreateSaveButtonSelector).click(); + cy.wait('@entitiesRequest').then(({ response }) => { + expect(response.statusCode).to.equal(200); + }); + cy.url().should('match', bloodPressurePageUrlPattern); + }); + + it('last delete button click should delete instance of BloodPressure', () => { + cy.get(entityDeleteButtonSelector).last().click(); + cy.getEntityDeleteDialogHeading('bloodPressure').should('exist'); + cy.get(entityConfirmDeleteButtonSelector).click(); + cy.wait('@deleteEntityRequest').then(({ response }) => { + expect(response.statusCode).to.equal(204); + }); + cy.wait('@entitiesRequest').then(({ response }) => { + expect(response.statusCode).to.equal(200); + }); + cy.url().should('match', bloodPressurePageUrlPattern); + + bloodPressure = undefined; + }); + }); + }); + + describe('new BloodPressure page', () => { + beforeEach(() => { + cy.visit(`${bloodPressurePageUrl}`); + cy.get(entityCreateButtonSelector).click(); + cy.getEntityCreateUpdateHeading('BloodPressure'); + }); + + it('should create an instance of BloodPressure', () => { + cy.get(`[data-cy="timestamp"]`).type('2022-11-07T07:53').blur().should('have.value', '2022-11-07T07:53'); + + cy.get(`[data-cy="systolic"]`).type('88377').should('have.value', '88377'); + + cy.get(`[data-cy="diastolic"]`).type('4724').should('have.value', '4724'); + + cy.get(entityCreateSaveButtonSelector).click(); + + cy.wait('@postEntityRequest').then(({ response }) => { + expect(response.statusCode).to.equal(201); + bloodPressure = response.body; + }); + cy.wait('@entitiesRequest').then(({ response }) => { + expect(response.statusCode).to.equal(200); + }); + cy.url().should('match', bloodPressurePageUrlPattern); + }); + }); +}); diff --git a/src/test/javascript/cypress/e2e/entity/points.cy.ts b/src/test/javascript/cypress/e2e/entity/points.cy.ts new file mode 100644 index 00000000..e130d551 --- /dev/null +++ b/src/test/javascript/cypress/e2e/entity/points.cy.ts @@ -0,0 +1,185 @@ +import { + entityTableSelector, + entityDetailsButtonSelector, + entityDetailsBackButtonSelector, + entityCreateButtonSelector, + entityCreateSaveButtonSelector, + entityCreateCancelButtonSelector, + entityEditButtonSelector, + entityDeleteButtonSelector, + entityConfirmDeleteButtonSelector, +} from '../../support/entity'; + +describe('Points e2e test', () => { + const pointsPageUrl = '/points'; + const pointsPageUrlPattern = new RegExp('/points(\\?.*)?$'); + const username = Cypress.env('E2E_USERNAME') ?? 'user'; + const password = Cypress.env('E2E_PASSWORD') ?? 'user'; + const pointsSample = { date: '2022-11-07' }; + + let points; + + beforeEach(() => { + cy.login(username, password); + }); + + beforeEach(() => { + cy.intercept('GET', '/api/points+(?*|)').as('entitiesRequest'); + cy.intercept('POST', '/api/points').as('postEntityRequest'); + cy.intercept('DELETE', '/api/points/*').as('deleteEntityRequest'); + }); + + afterEach(() => { + if (points) { + cy.authenticatedRequest({ + method: 'DELETE', + url: `/api/points/${points.id}`, + }).then(() => { + points = undefined; + }); + } + }); + + it('Points menu should load Points page', () => { + cy.visit('/'); + cy.clickOnEntityMenuItem('points'); + cy.wait('@entitiesRequest').then(({ response }) => { + if (response.body.length === 0) { + cy.get(entityTableSelector).should('not.exist'); + } else { + cy.get(entityTableSelector).should('exist'); + } + }); + cy.getEntityHeading('Points').should('exist'); + cy.url().should('match', pointsPageUrlPattern); + }); + + describe('Points page', () => { + describe('create button click', () => { + beforeEach(() => { + cy.visit(pointsPageUrl); + cy.wait('@entitiesRequest'); + }); + + it('should load create Points page', () => { + cy.get(entityCreateButtonSelector).click(); + cy.url().should('match', new RegExp('/points/new$')); + cy.getEntityCreateUpdateHeading('Points'); + cy.get(entityCreateSaveButtonSelector).should('exist'); + cy.get(entityCreateCancelButtonSelector).click(); + cy.wait('@entitiesRequest').then(({ response }) => { + expect(response.statusCode).to.equal(200); + }); + cy.url().should('match', pointsPageUrlPattern); + }); + }); + + describe('with existing value', () => { + beforeEach(() => { + cy.authenticatedRequest({ + method: 'POST', + url: '/api/points', + body: pointsSample, + }).then(({ body }) => { + points = body; + + cy.intercept( + { + method: 'GET', + url: '/api/points+(?*|)', + times: 1, + }, + { + statusCode: 200, + headers: { + link: '; rel="last",; rel="first"', + }, + body: [points], + } + ).as('entitiesRequestInternal'); + }); + + cy.visit(pointsPageUrl); + + cy.wait('@entitiesRequestInternal'); + }); + + it('detail button click should load details Points page', () => { + cy.get(entityDetailsButtonSelector).first().click(); + cy.getEntityDetailsHeading('points'); + cy.get(entityDetailsBackButtonSelector).click(); + cy.wait('@entitiesRequest').then(({ response }) => { + expect(response.statusCode).to.equal(200); + }); + cy.url().should('match', pointsPageUrlPattern); + }); + + it('edit button click should load edit Points page and go back', () => { + cy.get(entityEditButtonSelector).first().click(); + cy.getEntityCreateUpdateHeading('Points'); + cy.get(entityCreateSaveButtonSelector).should('exist'); + cy.get(entityCreateCancelButtonSelector).click(); + cy.wait('@entitiesRequest').then(({ response }) => { + expect(response.statusCode).to.equal(200); + }); + cy.url().should('match', pointsPageUrlPattern); + }); + + it('edit button click should load edit Points page and save', () => { + cy.get(entityEditButtonSelector).first().click(); + cy.getEntityCreateUpdateHeading('Points'); + cy.get(entityCreateSaveButtonSelector).click(); + cy.wait('@entitiesRequest').then(({ response }) => { + expect(response.statusCode).to.equal(200); + }); + cy.url().should('match', pointsPageUrlPattern); + }); + + it('last delete button click should delete instance of Points', () => { + cy.get(entityDeleteButtonSelector).last().click(); + cy.getEntityDeleteDialogHeading('points').should('exist'); + cy.get(entityConfirmDeleteButtonSelector).click(); + cy.wait('@deleteEntityRequest').then(({ response }) => { + expect(response.statusCode).to.equal(204); + }); + cy.wait('@entitiesRequest').then(({ response }) => { + expect(response.statusCode).to.equal(200); + }); + cy.url().should('match', pointsPageUrlPattern); + + points = undefined; + }); + }); + }); + + describe('new Points page', () => { + beforeEach(() => { + cy.visit(`${pointsPageUrl}`); + cy.get(entityCreateButtonSelector).click(); + cy.getEntityCreateUpdateHeading('Points'); + }); + + it('should create an instance of Points', () => { + cy.get(`[data-cy="date"]`).type('2022-11-07').blur().should('have.value', '2022-11-07'); + + cy.get(`[data-cy="exercise"]`).type('58383').should('have.value', '58383'); + + cy.get(`[data-cy="meals"]`).type('96341').should('have.value', '96341'); + + cy.get(`[data-cy="alcohol"]`).type('43411').should('have.value', '43411'); + + cy.get(`[data-cy="notes"]`).type('Response').should('have.value', 'Response'); + + cy.get(entityCreateSaveButtonSelector).click(); + + cy.wait('@postEntityRequest').then(({ response }) => { + expect(response.statusCode).to.equal(201); + points = response.body; + }); + cy.wait('@entitiesRequest').then(({ response }) => { + expect(response.statusCode).to.equal(200); + }); + cy.url().should('match', pointsPageUrlPattern); + }); + }); +}); diff --git a/src/test/javascript/cypress/e2e/entity/preferences.cy.ts b/src/test/javascript/cypress/e2e/entity/preferences.cy.ts new file mode 100644 index 00000000..4c187aaa --- /dev/null +++ b/src/test/javascript/cypress/e2e/entity/preferences.cy.ts @@ -0,0 +1,176 @@ +import { + entityTableSelector, + entityDetailsButtonSelector, + entityDetailsBackButtonSelector, + entityCreateButtonSelector, + entityCreateSaveButtonSelector, + entityCreateCancelButtonSelector, + entityEditButtonSelector, + entityDeleteButtonSelector, + entityConfirmDeleteButtonSelector, +} from '../../support/entity'; + +describe('Preferences e2e test', () => { + const preferencesPageUrl = '/preferences'; + const preferencesPageUrlPattern = new RegExp('/preferences(\\?.*)?$'); + const username = Cypress.env('E2E_USERNAME') ?? 'user'; + const password = Cypress.env('E2E_PASSWORD') ?? 'user'; + const preferencesSample = { weeklyGoal: 20, weightUnits: 'LB' }; + + let preferences; + + beforeEach(() => { + cy.login(username, password); + }); + + beforeEach(() => { + cy.intercept('GET', '/api/preferences+(?*|)').as('entitiesRequest'); + cy.intercept('POST', '/api/preferences').as('postEntityRequest'); + cy.intercept('DELETE', '/api/preferences/*').as('deleteEntityRequest'); + }); + + afterEach(() => { + if (preferences) { + cy.authenticatedRequest({ + method: 'DELETE', + url: `/api/preferences/${preferences.id}`, + }).then(() => { + preferences = undefined; + }); + } + }); + + it('Preferences menu should load Preferences page', () => { + cy.visit('/'); + cy.clickOnEntityMenuItem('preferences'); + cy.wait('@entitiesRequest').then(({ response }) => { + if (response.body.length === 0) { + cy.get(entityTableSelector).should('not.exist'); + } else { + cy.get(entityTableSelector).should('exist'); + } + }); + cy.getEntityHeading('Preferences').should('exist'); + cy.url().should('match', preferencesPageUrlPattern); + }); + + describe('Preferences page', () => { + describe('create button click', () => { + beforeEach(() => { + cy.visit(preferencesPageUrl); + cy.wait('@entitiesRequest'); + }); + + it('should load create Preferences page', () => { + cy.get(entityCreateButtonSelector).click(); + cy.url().should('match', new RegExp('/preferences/new$')); + cy.getEntityCreateUpdateHeading('Preferences'); + cy.get(entityCreateSaveButtonSelector).should('exist'); + cy.get(entityCreateCancelButtonSelector).click(); + cy.wait('@entitiesRequest').then(({ response }) => { + expect(response.statusCode).to.equal(200); + }); + cy.url().should('match', preferencesPageUrlPattern); + }); + }); + + describe('with existing value', () => { + beforeEach(() => { + cy.authenticatedRequest({ + method: 'POST', + url: '/api/preferences', + body: preferencesSample, + }).then(({ body }) => { + preferences = body; + + cy.intercept( + { + method: 'GET', + url: '/api/preferences+(?*|)', + times: 1, + }, + { + statusCode: 200, + body: [preferences], + } + ).as('entitiesRequestInternal'); + }); + + cy.visit(preferencesPageUrl); + + cy.wait('@entitiesRequestInternal'); + }); + + it('detail button click should load details Preferences page', () => { + cy.get(entityDetailsButtonSelector).first().click(); + cy.getEntityDetailsHeading('preferences'); + cy.get(entityDetailsBackButtonSelector).click(); + cy.wait('@entitiesRequest').then(({ response }) => { + expect(response.statusCode).to.equal(200); + }); + cy.url().should('match', preferencesPageUrlPattern); + }); + + it('edit button click should load edit Preferences page and go back', () => { + cy.get(entityEditButtonSelector).first().click(); + cy.getEntityCreateUpdateHeading('Preferences'); + cy.get(entityCreateSaveButtonSelector).should('exist'); + cy.get(entityCreateCancelButtonSelector).click(); + cy.wait('@entitiesRequest').then(({ response }) => { + expect(response.statusCode).to.equal(200); + }); + cy.url().should('match', preferencesPageUrlPattern); + }); + + it('edit button click should load edit Preferences page and save', () => { + cy.get(entityEditButtonSelector).first().click(); + cy.getEntityCreateUpdateHeading('Preferences'); + cy.get(entityCreateSaveButtonSelector).click(); + cy.wait('@entitiesRequest').then(({ response }) => { + expect(response.statusCode).to.equal(200); + }); + cy.url().should('match', preferencesPageUrlPattern); + }); + + it('last delete button click should delete instance of Preferences', () => { + cy.get(entityDeleteButtonSelector).last().click(); + cy.getEntityDeleteDialogHeading('preferences').should('exist'); + cy.get(entityConfirmDeleteButtonSelector).click(); + cy.wait('@deleteEntityRequest').then(({ response }) => { + expect(response.statusCode).to.equal(204); + }); + cy.wait('@entitiesRequest').then(({ response }) => { + expect(response.statusCode).to.equal(200); + }); + cy.url().should('match', preferencesPageUrlPattern); + + preferences = undefined; + }); + }); + }); + + describe('new Preferences page', () => { + beforeEach(() => { + cy.visit(`${preferencesPageUrl}`); + cy.get(entityCreateButtonSelector).click(); + cy.getEntityCreateUpdateHeading('Preferences'); + }); + + it('should create an instance of Preferences', () => { + cy.get(`[data-cy="weeklyGoal"]`).type('17').should('have.value', '17'); + + cy.get(`[data-cy="weightUnits"]`).select('LB'); + + cy.get(entityCreateSaveButtonSelector).click(); + + cy.wait('@postEntityRequest').then(({ response }) => { + expect(response.statusCode).to.equal(201); + preferences = response.body; + }); + cy.wait('@entitiesRequest').then(({ response }) => { + expect(response.statusCode).to.equal(200); + }); + cy.url().should('match', preferencesPageUrlPattern); + }); + }); +}); diff --git a/src/test/javascript/cypress/e2e/entity/weight.cy.ts b/src/test/javascript/cypress/e2e/entity/weight.cy.ts new file mode 100644 index 00000000..b47d4d1b --- /dev/null +++ b/src/test/javascript/cypress/e2e/entity/weight.cy.ts @@ -0,0 +1,179 @@ +import { + entityTableSelector, + entityDetailsButtonSelector, + entityDetailsBackButtonSelector, + entityCreateButtonSelector, + entityCreateSaveButtonSelector, + entityCreateCancelButtonSelector, + entityEditButtonSelector, + entityDeleteButtonSelector, + entityConfirmDeleteButtonSelector, +} from '../../support/entity'; + +describe('Weight e2e test', () => { + const weightPageUrl = '/weight'; + const weightPageUrlPattern = new RegExp('/weight(\\?.*)?$'); + const username = Cypress.env('E2E_USERNAME') ?? 'user'; + const password = Cypress.env('E2E_PASSWORD') ?? 'user'; + const weightSample = { timestamp: '2022-11-07T12:20:51.808Z', weight: 1792 }; + + let weight; + + beforeEach(() => { + cy.login(username, password); + }); + + beforeEach(() => { + cy.intercept('GET', '/api/weights+(?*|)').as('entitiesRequest'); + cy.intercept('POST', '/api/weights').as('postEntityRequest'); + cy.intercept('DELETE', '/api/weights/*').as('deleteEntityRequest'); + }); + + afterEach(() => { + if (weight) { + cy.authenticatedRequest({ + method: 'DELETE', + url: `/api/weights/${weight.id}`, + }).then(() => { + weight = undefined; + }); + } + }); + + it('Weights menu should load Weights page', () => { + cy.visit('/'); + cy.clickOnEntityMenuItem('weight'); + cy.wait('@entitiesRequest').then(({ response }) => { + if (response.body.length === 0) { + cy.get(entityTableSelector).should('not.exist'); + } else { + cy.get(entityTableSelector).should('exist'); + } + }); + cy.getEntityHeading('Weight').should('exist'); + cy.url().should('match', weightPageUrlPattern); + }); + + describe('Weight page', () => { + describe('create button click', () => { + beforeEach(() => { + cy.visit(weightPageUrl); + cy.wait('@entitiesRequest'); + }); + + it('should load create Weight page', () => { + cy.get(entityCreateButtonSelector).click(); + cy.url().should('match', new RegExp('/weight/new$')); + cy.getEntityCreateUpdateHeading('Weight'); + cy.get(entityCreateSaveButtonSelector).should('exist'); + cy.get(entityCreateCancelButtonSelector).click(); + cy.wait('@entitiesRequest').then(({ response }) => { + expect(response.statusCode).to.equal(200); + }); + cy.url().should('match', weightPageUrlPattern); + }); + }); + + describe('with existing value', () => { + beforeEach(() => { + cy.authenticatedRequest({ + method: 'POST', + url: '/api/weights', + body: weightSample, + }).then(({ body }) => { + weight = body; + + cy.intercept( + { + method: 'GET', + url: '/api/weights+(?*|)', + times: 1, + }, + { + statusCode: 200, + headers: { + link: '; rel="last",; rel="first"', + }, + body: [weight], + } + ).as('entitiesRequestInternal'); + }); + + cy.visit(weightPageUrl); + + cy.wait('@entitiesRequestInternal'); + }); + + it('detail button click should load details Weight page', () => { + cy.get(entityDetailsButtonSelector).first().click(); + cy.getEntityDetailsHeading('weight'); + cy.get(entityDetailsBackButtonSelector).click(); + cy.wait('@entitiesRequest').then(({ response }) => { + expect(response.statusCode).to.equal(200); + }); + cy.url().should('match', weightPageUrlPattern); + }); + + it('edit button click should load edit Weight page and go back', () => { + cy.get(entityEditButtonSelector).first().click(); + cy.getEntityCreateUpdateHeading('Weight'); + cy.get(entityCreateSaveButtonSelector).should('exist'); + cy.get(entityCreateCancelButtonSelector).click(); + cy.wait('@entitiesRequest').then(({ response }) => { + expect(response.statusCode).to.equal(200); + }); + cy.url().should('match', weightPageUrlPattern); + }); + + it('edit button click should load edit Weight page and save', () => { + cy.get(entityEditButtonSelector).first().click(); + cy.getEntityCreateUpdateHeading('Weight'); + cy.get(entityCreateSaveButtonSelector).click(); + cy.wait('@entitiesRequest').then(({ response }) => { + expect(response.statusCode).to.equal(200); + }); + cy.url().should('match', weightPageUrlPattern); + }); + + it('last delete button click should delete instance of Weight', () => { + cy.get(entityDeleteButtonSelector).last().click(); + cy.getEntityDeleteDialogHeading('weight').should('exist'); + cy.get(entityConfirmDeleteButtonSelector).click(); + cy.wait('@deleteEntityRequest').then(({ response }) => { + expect(response.statusCode).to.equal(204); + }); + cy.wait('@entitiesRequest').then(({ response }) => { + expect(response.statusCode).to.equal(200); + }); + cy.url().should('match', weightPageUrlPattern); + + weight = undefined; + }); + }); + }); + + describe('new Weight page', () => { + beforeEach(() => { + cy.visit(`${weightPageUrl}`); + cy.get(entityCreateButtonSelector).click(); + cy.getEntityCreateUpdateHeading('Weight'); + }); + + it('should create an instance of Weight', () => { + cy.get(`[data-cy="timestamp"]`).type('2022-11-07T06:09').blur().should('have.value', '2022-11-07T06:09'); + + cy.get(`[data-cy="weight"]`).type('90727').should('have.value', '90727'); + + cy.get(entityCreateSaveButtonSelector).click(); + + cy.wait('@postEntityRequest').then(({ response }) => { + expect(response.statusCode).to.equal(201); + weight = response.body; + }); + cy.wait('@entitiesRequest').then(({ response }) => { + expect(response.statusCode).to.equal(200); + }); + cy.url().should('match', weightPageUrlPattern); + }); + }); +}); diff --git a/src/test/javascript/cypress/fixtures/integration-test.png b/src/test/javascript/cypress/fixtures/integration-test.png new file mode 100644 index 0000000000000000000000000000000000000000..5d31c2f84d8484d68f7e154670a3d966f11a022e GIT binary patch literal 1085 zcmV-D1j74?P)T2QVR6rUYuXj1WIWS{r8-6zYZnO^Vo(; zvws2@q20fxo!WS-6CEgadt?A75g(0R$NZJ_Jf$SURGR%NPzNmFIOfnfFaw6b7#L4G zwOLYQbi;0sEW|xA0=*p_$e#EBDT$bL^n z60U%rYMx}bM`fTvHe!T%9u?Lig{d_A7T{)VP>=;8WcG?`MA+@o2||bl@G8ekU%HN2 z5AQr$b8t0VTkdcSya(P_n*9mTopx%yJT5G~qlNg*X@~73MBeLl9y2E2rzQ&Di@I5~ z?Ckc)_l~T?brN2?j=2&k?g%6MDL?bHDgl>>FgM)iB!O_%BKzF24If;`>;`v4g)%E2 zTejjHIIlGO*9Z^Vs<9viX~?aJaJ2~w&{7zmGvKV!?9YMLraYv9AU;-jM^q?NA-^Og zQlLOWKhNK!omt5zyz?Tom#_RaU%x; z4a7nZT`@|Y=TT|)w_A=`JPX8de>d(Rxgf8HTCDCyf4yFIESDhJXUNmwah zidwr|X7_^FiTv(Bckf7w40C=}_bx5l1)_?%72FwDgPEi#bdK<_A)+dIwu4~@gA~aM zY;obve~5cg_5Vrh{O|nff^G@C2xLx*8jIZcXAFMsxeMU0d%00000NkvXXu0mjf DEYbKo literal 0 HcmV?d00001 diff --git a/src/test/javascript/cypress/plugins/index.ts b/src/test/javascript/cypress/plugins/index.ts new file mode 100644 index 00000000..2fa72287 --- /dev/null +++ b/src/test/javascript/cypress/plugins/index.ts @@ -0,0 +1,23 @@ +// *********************************************************** +// This example plugins/index.js can be used to load plugins +// +// You can change the location of this file or turn off loading +// the plugins file with the 'pluginsFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/plugins-guide +// *********************************************************** + +// This function is called when a project is opened or re-opened (e.g. due to +// the project's config changing) + +export default async (on: Cypress.PluginEvents, config: Cypress.PluginConfigOptions) => { + on('before:browser:launch', (browser, launchOptions) => { + if (browser.name === 'chrome' && browser.isHeadless) { + launchOptions.args.push('--disable-gpu'); + return launchOptions; + } + }); + + return config; +}; diff --git a/src/test/javascript/cypress/support/commands.ts b/src/test/javascript/cypress/support/commands.ts new file mode 100644 index 00000000..8ca464cc --- /dev/null +++ b/src/test/javascript/cypress/support/commands.ts @@ -0,0 +1,126 @@ +/* eslint-disable @typescript-eslint/no-namespace */ +/* eslint-disable @typescript-eslint/no-use-before-define */ +// eslint-disable-next-line spaced-comment +/// + +// *********************************************** +// This commands.ts shows you how to +// create various custom commands and overwrite +// existing commands. +// +// For more comprehensive examples of custom +// commands please read more here: +// https://on.cypress.io/custom-commands +// *********************************************** + +// *********************************************** +// Begin Specific Selector Attributes for Cypress +// *********************************************** + +// Navbar +export const navbarSelector = '[data-cy="navbar"]'; +export const adminMenuSelector = '[data-cy="adminMenu"]'; +export const accountMenuSelector = '[data-cy="accountMenu"]'; +export const registerItemSelector = '[data-cy="register"]'; +export const settingsItemSelector = '[data-cy="settings"]'; +export const passwordItemSelector = '[data-cy="passwordItem"]'; +export const loginItemSelector = '[data-cy="login"]'; +export const logoutItemSelector = '[data-cy="logout"]'; +export const entityItemSelector = '[data-cy="entity"]'; + +// Login +export const titleLoginSelector = '[data-cy="loginTitle"]'; +export const errorLoginSelector = '[data-cy="loginError"]'; +export const usernameLoginSelector = '[data-cy="username"]'; +export const passwordLoginSelector = '[data-cy="password"]'; +export const forgetYourPasswordSelector = '[data-cy="forgetYourPasswordSelector"]'; +export const submitLoginSelector = '[data-cy="submit"]'; + +// Register +export const titleRegisterSelector = '[data-cy="registerTitle"]'; +export const usernameRegisterSelector = '[data-cy="username"]'; +export const emailRegisterSelector = '[data-cy="email"]'; +export const firstPasswordRegisterSelector = '[data-cy="firstPassword"]'; +export const secondPasswordRegisterSelector = '[data-cy="secondPassword"]'; +export const submitRegisterSelector = '[data-cy="submit"]'; + +// Settings +export const firstNameSettingsSelector = '[data-cy="firstname"]'; +export const lastNameSettingsSelector = '[data-cy="lastname"]'; +export const emailSettingsSelector = '[data-cy="email"]'; +export const languageSettingsSelector = '[data-cy="langKey"]'; +export const submitSettingsSelector = '[data-cy="submit"]'; + +// Password +export const currentPasswordSelector = '[data-cy="currentPassword"]'; +export const newPasswordSelector = '[data-cy="newPassword"]'; +export const confirmPasswordSelector = '[data-cy="confirmPassword"]'; +export const submitPasswordSelector = '[data-cy="submit"]'; + +// Reset Password +export const emailResetPasswordSelector = '[data-cy="emailResetPassword"]'; +export const submitInitResetPasswordSelector = '[data-cy="submit"]'; + +// Administration +export const userManagementPageHeadingSelector = '[data-cy="userManagementPageHeading"]'; +export const swaggerFrameSelector = 'iframe[data-cy="swagger-frame"]'; +export const swaggerPageSelector = '[id="swagger-ui"]'; +export const metricsPageHeadingSelector = '[data-cy="metricsPageHeading"]'; +export const healthPageHeadingSelector = '[data-cy="healthPageHeading"]'; +export const logsPageHeadingSelector = '[data-cy="logsPageHeading"]'; +export const configurationPageHeadingSelector = '[data-cy="configurationPageHeading"]'; + +// *********************************************** +// End Specific Selector Attributes for Cypress +// *********************************************** + +export const classInvalid = 'ng-invalid'; + +export const classValid = 'ng-valid'; + +Cypress.Commands.add('authenticatedRequest', data => { + const bearerToken = JSON.parse(sessionStorage.getItem(Cypress.env('jwtStorageName'))); + return cy.request({ + ...data, + auth: { + bearer: bearerToken, + }, + }); +}); + +Cypress.Commands.add('login', (username: string, password: string) => { + cy.session( + [username, password], + () => { + cy.request({ + method: 'GET', + url: '/api/account', + failOnStatusCode: false, + }); + cy.authenticatedRequest({ + method: 'POST', + body: { username, password }, + url: Cypress.env('authenticationUrl'), + }).then(({ body: { id_token } }) => { + sessionStorage.setItem(Cypress.env('jwtStorageName'), JSON.stringify(id_token)); + }); + }, + { + validate() { + cy.authenticatedRequest({ url: '/api/account' }).its('status').should('eq', 200); + }, + } + ); +}); + +declare global { + namespace Cypress { + interface Chainable { + authenticatedRequest(data): Cypress.Chainable; + login(username: string, password: string): Cypress.Chainable; + } + } +} + +// Convert this to a module instead of script (allows import/export) +export {}; diff --git a/src/test/javascript/cypress/support/entity.ts b/src/test/javascript/cypress/support/entity.ts new file mode 100644 index 00000000..e944772d --- /dev/null +++ b/src/test/javascript/cypress/support/entity.ts @@ -0,0 +1,87 @@ +/* eslint-disable @typescript-eslint/no-namespace */ +/* eslint-disable @typescript-eslint/no-use-before-define */ +// eslint-disable-next-line spaced-comment +/// + +// *********************************************** +// Begin Specific Selector Attributes for Cypress +// *********************************************** + +// Entity +export const entityTableSelector = '[data-cy="entityTable"]'; +export const entityCreateButtonSelector = '[data-cy="entityCreateButton"]'; +export const entityCreateSaveButtonSelector = '[data-cy="entityCreateSaveButton"]'; +export const entityCreateCancelButtonSelector = '[data-cy="entityCreateCancelButton"]'; +export const entityDetailsButtonSelector = '[data-cy="entityDetailsButton"]'; // can return multiple elements +export const entityDetailsBackButtonSelector = '[data-cy="entityDetailsBackButton"]'; +export const entityEditButtonSelector = '[data-cy="entityEditButton"]'; +export const entityDeleteButtonSelector = '[data-cy="entityDeleteButton"]'; +export const entityConfirmDeleteButtonSelector = '[data-cy="entityConfirmDeleteButton"]'; + +// *********************************************** +// End Specific Selector Attributes for Cypress +// *********************************************** + +Cypress.Commands.add('getEntityHeading', (entityName: string) => { + return cy.get(`[data-cy="${entityName}Heading"]`); +}); + +Cypress.Commands.add('getEntityCreateUpdateHeading', (entityName: string) => { + return cy.get(`[data-cy="${entityName}CreateUpdateHeading"]`); +}); + +Cypress.Commands.add('getEntityDetailsHeading', (entityInstanceName: string) => { + return cy.get(`[data-cy="${entityInstanceName}DetailsHeading"]`); +}); + +Cypress.Commands.add('getEntityDeleteDialogHeading', (entityInstanceName: string) => { + return cy.get(`[data-cy="${entityInstanceName}DeleteDialogHeading"]`); +}); + +Cypress.Commands.add('setFieldImageAsBytesOfEntity', (fieldName: string, fileName: string, mimeType: string) => { + // fileName is the image which you have already put in cypress fixture folder + // should be like : 'integration-test.png', 'image/png' + cy.fixture(fileName) + .as('image') + .get(`[data-cy="${fieldName}"]`) + .then(function (el) { + const blob = Cypress.Blob.base64StringToBlob(this.image, mimeType); + const file = new File([blob], fileName, { type: mimeType }); + const list = new DataTransfer(); + list.items.add(file); + const myFileList = list.files; + (el[0] as HTMLInputElement).files = myFileList; + el[0].dispatchEvent(new Event('change', { bubbles: true })); + }); +}); + +Cypress.Commands.add('setFieldSelectToLastOfEntity', (fieldName: string) => { + return cy.get(`[data-cy="${fieldName}"]`).then(select => { + const selectSize = (select[0] as HTMLSelectElement)?.options?.length || Number(select.attr('size')) || 0; + if (selectSize > 0) { + return cy.get(`[data-cy="${fieldName}"] option`).then((options: JQuery) => { + const elements = [...options].map((o: HTMLElement) => (o as HTMLOptionElement).label); + const lastElement = elements.length - 1; + cy.get(`[data-cy="${fieldName}"]`).select(lastElement).type('{downarrow}'); + }); + } else { + return cy.get(`[data-cy="${fieldName}"]`).type('{downarrow}'); + } + }); +}); + +declare global { + namespace Cypress { + interface Chainable { + getEntityHeading(entityName: string): Cypress.Chainable; + getEntityCreateUpdateHeading(entityName: string): Cypress.Chainable; + getEntityDetailsHeading(entityInstanceName: string): Cypress.Chainable; + getEntityDeleteDialogHeading(entityInstanceName: string): Cypress.Chainable; + setFieldImageAsBytesOfEntity(fieldName: string, fileName: string, mimeType: string): Cypress.Chainable; + setFieldSelectToLastOfEntity(fieldName: string): Cypress.Chainable; + } + } +} + +// Convert this to a module instead of script (allows import/export) +export {}; diff --git a/src/test/javascript/cypress/support/index.ts b/src/test/javascript/cypress/support/index.ts new file mode 100644 index 00000000..1a443005 --- /dev/null +++ b/src/test/javascript/cypress/support/index.ts @@ -0,0 +1,19 @@ +// *********************************************************** +// This support/index.js is processed and +// loaded automatically before your test files. +// +// This is a great place to put global configuration and +// behavior that modifies Cypress. +// +// You can change the location of this file or turn off +// automatically serving support files with the +// 'supportFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/configuration +// *********************************************************** + +import './commands'; +import './navbar'; +import './entity'; +import './management'; diff --git a/src/test/javascript/cypress/support/management.ts b/src/test/javascript/cypress/support/management.ts new file mode 100644 index 00000000..b8d87015 --- /dev/null +++ b/src/test/javascript/cypress/support/management.ts @@ -0,0 +1,22 @@ +/* eslint-disable @typescript-eslint/no-namespace */ +/* eslint-disable @typescript-eslint/no-use-before-define */ + +Cypress.Commands.add('getManagementInfo', () => { + return cy + .request({ + method: 'GET', + url: '/management/info', + }) + .then(response => response.body); +}); + +declare global { + namespace Cypress { + interface Chainable { + getManagementInfo(): Cypress.Chainable; + } + } +} + +// Convert this to a module instead of script (allows import/export) +export {}; diff --git a/src/test/javascript/cypress/support/navbar.ts b/src/test/javascript/cypress/support/navbar.ts new file mode 100644 index 00000000..15032503 --- /dev/null +++ b/src/test/javascript/cypress/support/navbar.ts @@ -0,0 +1,59 @@ +/* eslint-disable @typescript-eslint/no-namespace */ +/* eslint-disable @typescript-eslint/no-use-before-define */ + +import { + navbarSelector, + adminMenuSelector, + accountMenuSelector, + registerItemSelector, + loginItemSelector, + logoutItemSelector, + settingsItemSelector, + passwordItemSelector, + entityItemSelector, +} from './commands'; + +Cypress.Commands.add('clickOnLoginItem', () => { + return cy.get(navbarSelector).get(accountMenuSelector).click().get(loginItemSelector).click(); +}); + +Cypress.Commands.add('clickOnLogoutItem', () => { + return cy.get(navbarSelector).get(accountMenuSelector).click().get(logoutItemSelector).click(); +}); + +Cypress.Commands.add('clickOnRegisterItem', () => { + return cy.get(navbarSelector).get(accountMenuSelector).click().get(registerItemSelector).click(); +}); + +Cypress.Commands.add('clickOnSettingsItem', () => { + return cy.get(navbarSelector).get(accountMenuSelector).click().get(settingsItemSelector).click(); +}); + +Cypress.Commands.add('clickOnPasswordItem', () => { + return cy.get(navbarSelector).get(accountMenuSelector).click().get(passwordItemSelector).click(); +}); + +Cypress.Commands.add('clickOnAdminMenuItem', (item: string) => { + return cy.get(navbarSelector).get(adminMenuSelector).click().get(`.dropdown-item[href="/admin/${item}"]`).click(); +}); + +Cypress.Commands.add('clickOnEntityMenuItem', (entityName: string) => { + return cy.get(navbarSelector).get(entityItemSelector).click().get(`.dropdown-item[href="/${entityName}"]`).click({ force: true }); +}); + +declare global { + namespace Cypress { + interface Chainable { + clickOnLoginItem(): Cypress.Chainable; + clickOnLogoutItem(): Cypress.Chainable; + clickOnRegisterItem(): Cypress.Chainable; + clickOnSettingsItem(): Cypress.Chainable; + clickOnPasswordItem(): Cypress.Chainable; + clickOnAdminMenuItem(item: string): Cypress.Chainable; + clickOnEntityMenuItem(entityName: string): Cypress.Chainable; + } + } +} + +// Convert this to a module instead of script (allows import/export) +export {}; diff --git a/src/test/javascript/cypress/tsconfig.json b/src/test/javascript/cypress/tsconfig.json new file mode 100644 index 00000000..9be4b828 --- /dev/null +++ b/src/test/javascript/cypress/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "../../../../tsconfig.json", + "compilerOptions": { + "baseUrl": "./", + "outDir": "../../../../build/out-tsc/cypress", + "target": "es2018", + "types": ["cypress", "node"] + }, + "include": ["./../../../../cypress*.config.ts", "./**/*.ts"] +} diff --git a/src/test/resources/META-INF/spring.factories b/src/test/resources/META-INF/spring.factories new file mode 100644 index 00000000..3f4035c0 --- /dev/null +++ b/src/test/resources/META-INF/spring.factories @@ -0,0 +1 @@ +org.springframework.test.context.ContextCustomizerFactory = org.jhipster.health.config.TestContainersSpringContextCustomizerFactory diff --git a/src/test/resources/config/application-testdev.yml b/src/test/resources/config/application-testdev.yml new file mode 100644 index 00000000..98d28d0f --- /dev/null +++ b/src/test/resources/config/application-testdev.yml @@ -0,0 +1,31 @@ +# =================================================================== +# Spring Boot configuration. +# +# This configuration is used for unit/integration tests with testcontainers database containers. +# +# To activate this configuration launch integration tests with the 'testcontainers' profile +# +# More information on database containers: https://www.testcontainers.org/modules/databases/ +# =================================================================== + +spring: + datasource: + type: com.zaxxer.hikari.HikariDataSource + hikari: + auto-commit: false + jpa: + open-in-view: false + hibernate: + ddl-auto: none + naming: + physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy + implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy + properties: + hibernate.id.new_generator_mappings: true + hibernate.connection.provider_disables_autocommit: true + hibernate.cache.use_second_level_cache: false + hibernate.cache.use_query_cache: false + hibernate.generate_statistics: false + hibernate.hbm2ddl.auto: validate + hibernate.jdbc.time_zone: UTC + hibernate.query.fail_on_pagination_over_collection_fetch: true diff --git a/src/test/resources/config/application-testprod.yml b/src/test/resources/config/application-testprod.yml new file mode 100644 index 00000000..484eda81 --- /dev/null +++ b/src/test/resources/config/application-testprod.yml @@ -0,0 +1,34 @@ +# =================================================================== +# Spring Boot configuration. +# +# This configuration is used for unit/integration tests with testcontainers database containers. +# +# To activate this configuration launch integration tests with the 'testcontainers' profile +# +# More information on database containers: https://www.testcontainers.org/modules/databases/ +# =================================================================== + +spring: + datasource: + type: com.zaxxer.hikari.HikariDataSource + hikari: + poolName: Hikari + auto-commit: false + maximum-pool-size: 1 + jpa: + database-platform: tech.jhipster.domain.util.FixedPostgreSQL10Dialect + open-in-view: false + hibernate: + ddl-auto: none + naming: + physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy + implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy + properties: + hibernate.id.new_generator_mappings: true + hibernate.connection.provider_disables_autocommit: true + hibernate.cache.use_second_level_cache: false + hibernate.cache.use_query_cache: false + hibernate.generate_statistics: false + hibernate.hbm2ddl.auto: validate + hibernate.jdbc.time_zone: UTC + hibernate.query.fail_on_pagination_over_collection_fetch: true diff --git a/src/test/resources/config/application.yml b/src/test/resources/config/application.yml new file mode 100644 index 00000000..29f66e0d --- /dev/null +++ b/src/test/resources/config/application.yml @@ -0,0 +1,92 @@ +# =================================================================== +# Spring Boot configuration. +# +# This configuration is used for unit/integration tests. +# +# More information on profiles: https://www.jhipster.tech/profiles/ +# More information on configuration properties: https://www.jhipster.tech/common-application-properties/ +# =================================================================== + +# =================================================================== +# Standard Spring Boot properties. +# Full reference is available at: +# http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html +# =================================================================== + +spring: + application: + name: TwentyOnePoints + # Replace by 'prod, faker' to add the faker context and have sample data loaded in production + liquibase: + contexts: test + jackson: + serialization: + write-durations-as-timestamps: false + mail: + host: localhost + main: + allow-bean-definition-overriding: true + mvc: + pathmatch: + matching-strategy: ant_path_matcher + messages: + basename: i18n/messages + task: + execution: + thread-name-prefix: twenty-one-points-task- + pool: + core-size: 1 + max-size: 50 + queue-capacity: 10000 + scheduling: + thread-name-prefix: twenty-one-points-scheduling- + pool: + size: 20 + thymeleaf: + mode: HTML + +server: + port: 10344 + address: localhost + +# =================================================================== +# JHipster specific properties +# +# Full reference is available at: https://www.jhipster.tech/common-application-properties/ +# =================================================================== +jhipster: + clientApp: + name: 'twentyOnePointsApp' + mail: + from: TwentyOnePoints@localhost.com + base-url: http://127.0.0.1:8080 + logging: + # To test json console appender + use-json-format: false + logstash: + enabled: false + host: localhost + port: 5000 + queue-size: 512 + security: + authentication: + jwt: + # This token must be encoded using Base64 (you can type `echo 'secret-key'|base64` on your command line) + base64-secret: YjAwZDU2ODI3NzNjZjk0NjVhY2UzNGViZmY0YjY1ZGY2MDI5MTc5Y2FlZWYxNzE5Yjg5ZjMxOGZhZTgwZjA5NTFkN2VmNDM3ZGMyZDNjZTViNDAwYTg4NmNlMmM2ZDkxZTMwMDk5YmUxNWNkZmE0YTNiNDlhMGNhZmM4OTk1NmQ= + # Token is valid 24 hours + token-validity-in-seconds: 86400 + +# =================================================================== +# Application specific properties +# Add your own application properties here, see the ApplicationProperties class +# to have type-safe configuration, like in the JHipsterProperties above +# +# More documentation is available at: +# https://www.jhipster.tech/common-application-properties/ +# =================================================================== + +# application: +management: + health: + mail: + enabled: false diff --git a/src/test/resources/config/bootstrap.yml b/src/test/resources/config/bootstrap.yml new file mode 100644 index 00000000..e69de29b diff --git a/src/test/resources/i18n/messages_en.properties b/src/test/resources/i18n/messages_en.properties new file mode 100644 index 00000000..ce4056bc --- /dev/null +++ b/src/test/resources/i18n/messages_en.properties @@ -0,0 +1,4 @@ +email.test.title=test title +# Value used for English locale unit test in MailServiceIT +# as this file is loaded instead of real file +email.activation.title=TwentyOnePoints account activation diff --git a/src/test/resources/i18n/messages_fr.properties b/src/test/resources/i18n/messages_fr.properties new file mode 100644 index 00000000..7ff49fb5 --- /dev/null +++ b/src/test/resources/i18n/messages_fr.properties @@ -0,0 +1,4 @@ +email.test.title=test title +# Value used for English locale unit test in MailServiceIT +# as this file is loaded instead of real file +email.activation.title=Activation de votre compte TwentyOnePoints \ No newline at end of file diff --git a/src/test/resources/junit-platform.properties b/src/test/resources/junit-platform.properties new file mode 100644 index 00000000..dbe738d2 --- /dev/null +++ b/src/test/resources/junit-platform.properties @@ -0,0 +1,4 @@ +junit.jupiter.execution.timeout.default = 15 s +junit.jupiter.execution.timeout.testable.method.default = 15 s +junit.jupiter.execution.timeout.beforeall.method.default = 60 s +junit.jupiter.testclass.order.default=org.jhipster.health.config.SpringBootTestClassOrderer diff --git a/src/test/resources/logback.xml b/src/test/resources/logback.xml new file mode 100644 index 00000000..79dec298 --- /dev/null +++ b/src/test/resources/logback.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + WARN + + + + diff --git a/src/test/resources/templates/mail/activationEmail.html b/src/test/resources/templates/mail/activationEmail.html new file mode 100644 index 00000000..01ad64c7 --- /dev/null +++ b/src/test/resources/templates/mail/activationEmail.html @@ -0,0 +1,19 @@ + + + + JHipster activation + + + +

Dear

+

Your JHipster account has been created, please click on the URL below to activate it:

+

+ Activation link +

+

+ Regards, +
+ JHipster. +

+ + diff --git a/src/test/resources/templates/mail/creationEmail.html b/src/test/resources/templates/mail/creationEmail.html new file mode 100644 index 00000000..e96075f4 --- /dev/null +++ b/src/test/resources/templates/mail/creationEmail.html @@ -0,0 +1,19 @@ + + + + JHipster creation + + + +

Dear

+

Your JHipster account has been created, please click on the URL below to access it:

+

+ Login link +

+

+ Regards, +
+ JHipster. +

+ + diff --git a/src/test/resources/templates/mail/passwordResetEmail.html b/src/test/resources/templates/mail/passwordResetEmail.html new file mode 100644 index 00000000..ca7727a8 --- /dev/null +++ b/src/test/resources/templates/mail/passwordResetEmail.html @@ -0,0 +1,21 @@ + + + + JHipster password reset + + + +

Dear

+

+ For your JHipster account a password reset was requested, please click on the URL below to reset it: +

+

+ Login link +

+

+ Regards, +
+ JHipster. +

+ + diff --git a/src/test/resources/templates/mail/testEmail.html b/src/test/resources/templates/mail/testEmail.html new file mode 100644 index 00000000..a4ca16a7 --- /dev/null +++ b/src/test/resources/templates/mail/testEmail.html @@ -0,0 +1 @@ + diff --git a/src/test/resources/testcontainers.properties b/src/test/resources/testcontainers.properties new file mode 100644 index 00000000..f0e6e42c --- /dev/null +++ b/src/test/resources/testcontainers.properties @@ -0,0 +1 @@ +testcontainers.reuse.enable=true diff --git a/tsconfig.app.json b/tsconfig.app.json new file mode 100644 index 00000000..8ed73e4c --- /dev/null +++ b/tsconfig.app.json @@ -0,0 +1,9 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "./build/resources/main/static/app", + "types": [] + }, + "files": ["src/main/webapp/main.ts", "src/main/webapp/polyfills.ts"], + "include": ["src/main/webapp/**/*.d.ts"] +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 00000000..fb0c3d34 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,28 @@ +{ + "compilerOptions": { + "baseUrl": "src/main/webapp/", + "outDir": "./build/resources/main/static/", + "forceConsistentCasingInFileNames": true, + "strict": true, + "strictNullChecks": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "sourceMap": true, + "declaration": false, + "downlevelIteration": true, + "experimentalDecorators": true, + "moduleResolution": "node", + "importHelpers": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true, + "target": "es2020", + "module": "es2020", + "lib": ["es2018", "dom"] + }, + "angularCompilerOptions": { + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true, + "preserveWhitespaces": true + } +} diff --git a/tsconfig.spec.json b/tsconfig.spec.json new file mode 100644 index 00000000..8ad6896a --- /dev/null +++ b/tsconfig.spec.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/main/webapp/**/*.ts"], + "compilerOptions": { + "outDir": "build/out-tsc/spec", + "types": ["jest", "node"] + } +} diff --git a/webpack/environment.js b/webpack/environment.js new file mode 100644 index 00000000..d0211776 --- /dev/null +++ b/webpack/environment.js @@ -0,0 +1,6 @@ +module.exports = { + I18N_HASH: 'generated_hash', + SERVER_API_URL: '', + __VERSION__: process.env.hasOwnProperty('APP_VERSION') ? process.env.APP_VERSION : 'DEV', + __DEBUG_INFO_ENABLED__: false, +}; diff --git a/webpack/logo-jhipster.png b/webpack/logo-jhipster.png new file mode 100644 index 0000000000000000000000000000000000000000..e301aa90f75e3ef8b5c5bf053186f3a02e7605d2 GIT binary patch literal 3326 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 EKQ4Pe { + const languagesHash = await hashElement(path.resolve(__dirname, '../src/main/webapp/i18n'), { + algo: 'md5', + encoding: 'hex', + files: { include: ['*.json'] }, + }); + + // PLUGINS + if (config.mode === 'development') { + config.plugins.push( + new ESLintPlugin({ + baseConfig: { + parserOptions: { + project: ['../tsconfig.app.json'], + }, + }, + }), + new WebpackNotifierPlugin({ + title: 'Twenty One Points', + contentImage: path.join(__dirname, 'logo-jhipster.png'), + }) + ); + } + + // configuring proxy for back end service + const tls = Boolean(config.devServer && config.devServer.https); + if (config.devServer) { + config.devServer.proxy = proxyConfig({ tls }); + } + + if (targetOptions.target === 'serve' || config.watch) { + config.plugins.push( + new BrowserSyncPlugin( + { + host: 'localhost', + port: 9000, + https: tls, + proxy: { + target: `http${tls ? 's' : ''}://localhost:${targetOptions.target === 'serve' ? '4200' : '8080'}`, + ws: true, + proxyOptions: { + changeOrigin: false, //pass the Host header to the backend unchanged https://github.com/Browsersync/browser-sync/issues/430 + }, + }, + socket: { + clients: { + heartbeatTimeout: 60000, + }, + }, + /* + ghostMode: { // uncomment this part to disable BrowserSync ghostMode; https://github.com/jhipster/generator-jhipster/issues/11116 + clicks: false, + location: false, + forms: false, + scroll: false, + }, + */ + }, + { + reload: targetOptions.target === 'build', // enabled for build --watch + } + ) + ); + } + + if (config.mode === 'production') { + config.plugins.push( + new BundleAnalyzerPlugin({ + analyzerMode: 'static', + openAnalyzer: false, + // Webpack statistics in target folder + reportFilename: '../stats.html', + }) + ); + } + + const patterns = [ + { + // https://github.com/swagger-api/swagger-ui/blob/v4.6.1/swagger-ui-dist-package/README.md + context: require('swagger-ui-dist').getAbsoluteFSPath(), + from: '*.{js,css,html,png}', + to: 'swagger-ui/', + globOptions: { ignore: ['**/index.html'] }, + }, + { + from: require.resolve('axios/dist/axios.min.js'), + to: 'swagger-ui/', + }, + { from: './src/main/webapp/swagger-ui/', to: 'swagger-ui/' }, + // jhipster-needle-add-assets-to-webpack - JHipster will add/remove third-party resources in this array + ]; + + if (patterns.length > 0) { + config.plugins.push(new CopyWebpackPlugin({ patterns })); + } + + config.plugins.push( + new webpack.DefinePlugin({ + I18N_HASH: JSON.stringify(languagesHash.hash), + // APP_VERSION is passed as an environment variable from the Gradle / Maven build tasks. + __VERSION__: JSON.stringify(environment.__VERSION__), + __DEBUG_INFO_ENABLED__: environment.__DEBUG_INFO_ENABLED__ || config.mode === 'development', + // The root URL for API calls, ending with a '/' - for example: `"https://www.jhipster.tech:8081/myservice/"`. + // If this URL is left empty (""), then it will be relative to the current context. + // If you use an API server, in `prod` mode, you will need to enable CORS + // (see the `jhipster.cors` common JHipster property in the `application-*.yml` configurations) + SERVER_API_URL: JSON.stringify(environment.SERVER_API_URL), + }), + new MergeJsonWebpackPlugin({ + output: { + groupBy: [ + { pattern: './src/main/webapp/i18n/en/*.json', fileName: './i18n/en.json' }, + { pattern: './src/main/webapp/i18n/fr/*.json', fileName: './i18n/fr.json' }, + // jhipster-needle-i18n-language-webpack - JHipster will add/remove languages in this array + ], + }, + }) + ); + + config = merge( + config + // jhipster-needle-add-webpack-config - JHipster will add custom config + ); + + return config; +}; From a6b7ae4fc7055ba304edc346bf4dc5b48966bc86 Mon Sep 17 00:00:00 2001 From: Matt Raible Date: Thu, 9 May 2024 09:55:11 -0600 Subject: [PATCH 02/17] apply updated prettier to source application --- .../jhipster/health/TwentyOnePointsApp.java | 3 +- .../health/aop/logging/LoggingAspect.java | 26 +++++---- .../health/config/CacheConfiguration.java | 16 +++--- .../java/org/jhipster/health/domain/User.java | 1 - .../management/SecurityMetersService.java | 3 +- .../search/BloodPressureSearchRepository.java | 8 --- .../search/PointsSearchRepository.java | 8 --- .../search/PreferencesSearchRepository.java | 4 -- .../search/WeightSearchRepository.java | 8 --- .../health/security/SecurityUtils.java | 3 +- .../health/security/jwt/TokenProvider.java | 10 ++-- .../service/ElasticsearchIndexService.java | 3 +- .../jhipster/health/service/UserService.java | 9 ++-- .../health/service/mapper/UserMapper.java | 17 +++--- .../health/web/rest/AccountResource.java | 3 +- .../web/rest/BloodPressureResource.java | 12 ++--- .../web/rest/ElasticsearchIndexResource.java | 3 +- .../health/web/rest/PointsResource.java | 12 ++--- .../health/web/rest/PreferencesResource.java | 9 ++-- .../health/web/rest/UserResource.java | 5 +- .../health/web/rest/WeightResource.java | 12 ++--- .../web/rest/errors/ExceptionTranslator.java | 31 +++++------ src/main/resources/config/application.yml | 2 +- src/main/resources/templates/error.html | 6 ++- .../templates/mail/activationEmail.html | 2 +- .../templates/mail/creationEmail.html | 2 +- .../templates/mail/passwordResetEmail.html | 2 +- src/main/webapp/404.html | 2 +- .../activate/activate.component.spec.ts | 6 +-- .../account/activate/activate.component.ts | 5 +- .../account/activate/activate.service.spec.ts | 2 +- .../app/account/activate/activate.service.ts | 5 +- .../password-reset-finish.component.spec.ts | 4 +- .../finish/password-reset-finish.component.ts | 5 +- .../password-reset-finish.service.spec.ts | 2 +- .../finish/password-reset-finish.service.ts | 5 +- .../password-reset-init.component.spec.ts | 2 +- .../init/password-reset-init.component.ts | 5 +- .../init/password-reset-init.service.spec.ts | 2 +- .../init/password-reset-init.service.ts | 5 +- .../password-strength-bar.component.ts | 5 +- .../account/password/password.component.ts | 5 +- .../account/password/password.service.spec.ts | 2 +- .../app/account/password/password.service.ts | 5 +- .../register/register.component.spec.ts | 14 ++--- .../account/register/register.component.ts | 5 +- .../app/account/register/register.model.ts | 7 ++- .../account/register/register.service.spec.ts | 2 +- .../app/account/register/register.service.ts | 5 +- .../account/settings/settings.component.ts | 5 +- .../configuration/configuration.service.ts | 11 ++-- .../elasticsearch-reindex-modal.component.ts | 5 +- ...search-reindex-selected-modal.component.ts | 5 +- .../app/admin/health/health.component.ts | 5 +- .../app/admin/health/health.service.spec.ts | 2 +- .../webapp/app/admin/health/health.service.ts | 5 +- src/main/webapp/app/admin/logs/log.model.ts | 5 +- .../webapp/app/admin/logs/logs.component.html | 2 +- .../app/admin/logs/logs.component.spec.ts | 4 +- .../webapp/app/admin/logs/logs.component.ts | 2 +- .../webapp/app/admin/logs/logs.service.ts | 5 +- .../app/admin/metrics/metrics.component.ts | 5 +- .../app/admin/metrics/metrics.service.ts | 5 +- ...management-delete-dialog.component.spec.ts | 2 +- ...user-management-delete-dialog.component.ts | 5 +- .../user-management-detail.component.spec.ts | 2 +- .../list/user-management.component.spec.ts | 16 +++--- .../list/user-management.component.ts | 2 +- .../service/user-management.service.ts | 5 +- .../user-management-update.component.spec.ts | 6 +-- .../user-management-update.component.ts | 5 +- .../user-management/user-management.model.ts | 2 +- src/main/webapp/app/app-routing.module.ts | 6 +-- src/main/webapp/app/app.module.ts | 2 +- .../webapp/app/core/auth/account.model.ts | 2 +- .../webapp/app/core/auth/account.service.ts | 6 +-- .../webapp/app/core/auth/auth-jwt.service.ts | 4 +- .../core/auth/user-route-access.service.ts | 8 ++- .../interceptor/auth-expired.interceptor.ts | 4 +- .../app/core/interceptor/auth.interceptor.ts | 2 +- .../interceptor/error-handler.interceptor.ts | 2 +- .../interceptor/notification.interceptor.ts | 2 +- .../app/core/util/alert.service.spec.ts | 54 +++++++++---------- .../webapp/app/core/util/alert.service.ts | 6 ++- .../core/util/event-manager.service.spec.ts | 2 +- .../app/core/util/event-manager.service.ts | 7 ++- ...d-pressure-delete-dialog.component.spec.ts | 2 +- .../blood-pressure-delete-dialog.component.ts | 7 ++- .../list/blood-pressure.component.spec.ts | 8 +-- .../list/blood-pressure.component.ts | 14 ++--- .../blood-pressure-routing-resolve.service.ts | 7 ++- .../route/blood-pressure-routing.module.ts | 2 +- .../service/blood-pressure.service.ts | 7 ++- .../blood-pressure-form.service.spec.ts | 4 +- .../update/blood-pressure-form.service.ts | 8 +-- .../blood-pressure-update.component.spec.ts | 8 +-- .../update/blood-pressure-update.component.ts | 8 +-- .../points-delete-dialog.component.spec.ts | 2 +- .../delete/points-delete-dialog.component.ts | 7 ++- .../points/list/points.component.spec.ts | 8 +-- .../entities/points/list/points.component.ts | 10 ++-- .../route/points-routing-resolve.service.ts | 7 ++- .../points/route/points-routing.module.ts | 2 +- .../entities/points/service/points.service.ts | 5 +- .../points/update/points-form.service.spec.ts | 4 +- .../points/update/points-form.service.ts | 4 +- .../update/points-update.component.spec.ts | 8 +-- .../points/update/points-update.component.ts | 8 +-- ...references-delete-dialog.component.spec.ts | 2 +- .../preferences-delete-dialog.component.ts | 7 ++- .../list/preferences.component.spec.ts | 6 +-- .../preferences/list/preferences.component.ts | 10 ++-- .../preferences-routing-resolve.service.ts | 7 ++- .../route/preferences-routing.module.ts | 2 +- .../service/preferences.service.ts | 7 ++- .../update/preferences-form.service.spec.ts | 4 +- .../update/preferences-form.service.ts | 4 +- .../preferences-update.component.spec.ts | 8 +-- .../update/preferences-update.component.ts | 8 +-- .../webapp/app/entities/user/user.model.ts | 5 +- .../webapp/app/entities/user/user.service.ts | 5 +- .../weight-delete-dialog.component.spec.ts | 2 +- .../delete/weight-delete-dialog.component.ts | 7 ++- .../weight/list/weight.component.spec.ts | 8 +-- .../entities/weight/list/weight.component.ts | 14 ++--- .../route/weight-routing-resolve.service.ts | 7 ++- .../weight/route/weight-routing.module.ts | 2 +- .../entities/weight/service/weight.service.ts | 5 +- .../weight/update/weight-form.service.spec.ts | 4 +- .../weight/update/weight-form.service.ts | 6 +-- .../update/weight-update.component.spec.ts | 8 +-- .../weight/update/weight-update.component.ts | 8 +-- src/main/webapp/app/home/home.component.ts | 5 +- .../app/layouts/error/error.component.ts | 5 +- .../webapp/app/layouts/main/main.component.ts | 2 +- .../layouts/navbar/active-menu.directive.ts | 6 ++- .../app/layouts/navbar/navbar.component.ts | 2 +- .../layouts/profiles/profile-info.model.ts | 2 +- .../app/layouts/profiles/profile.service.ts | 7 ++- src/main/webapp/app/login/login.component.ts | 6 ++- src/main/webapp/app/login/login.model.ts | 6 ++- src/main/webapp/app/login/login.service.ts | 5 +- .../app/shared/alert/alert-error.component.ts | 12 +++-- .../app/shared/alert/alert-error.model.ts | 6 ++- .../auth/has-any-authority.directive.ts | 6 ++- .../webapp/app/shared/filter/filter.model.ts | 5 +- .../shared/language/translate.directive.ts | 5 +- .../app/shared/language/translation.module.ts | 5 +- src/main/webapp/index.html | 2 +- src/main/webapp/swagger-ui/index.html | 2 +- .../org/jhipster/health/IntegrationTest.java | 1 - .../health/config/AsyncSyncConfiguration.java | 1 - .../config/ElasticsearchTestContainer.java | 17 +++--- .../config/PostgreSqlTestContainer.java | 11 ++-- ...tainersSpringContextCustomizerFactory.java | 15 +++--- .../health/config/WebConfigurerTest.java | 1 - .../security/DomainUserDetailsServiceIT.java | 5 +- .../jwt/TokenProviderSecurityMetersTests.java | 3 +- .../security/jwt/TokenProviderTest.java | 3 +- .../health/service/MailServiceIT.java | 5 +- .../health/service/UserServiceIT.java | 4 -- .../health/web/rest/AccountResourceIT.java | 1 - .../web/rest/BloodPressureResourceIT.java | 2 - .../health/web/rest/PointsResourceIT.java | 2 - .../web/rest/PreferencesResourceIT.java | 3 -- .../health/web/rest/WeightResourceIT.java | 2 - .../rest/errors/ExceptionTranslatorIT.java | 1 - .../cypress/e2e/entity/blood-pressure.cy.ts | 2 +- .../cypress/e2e/entity/points.cy.ts | 2 +- .../cypress/e2e/entity/preferences.cy.ts | 2 +- .../cypress/e2e/entity/weight.cy.ts | 2 +- .../javascript/cypress/support/commands.ts | 2 +- .../templates/mail/activationEmail.html | 2 +- .../templates/mail/creationEmail.html | 2 +- .../templates/mail/passwordResetEmail.html | 2 +- webpack/webpack.custom.js | 12 ++--- 176 files changed, 552 insertions(+), 466 deletions(-) diff --git a/src/main/java/org/jhipster/health/TwentyOnePointsApp.java b/src/main/java/org/jhipster/health/TwentyOnePointsApp.java index c83a4b82..7399891b 100644 --- a/src/main/java/org/jhipster/health/TwentyOnePointsApp.java +++ b/src/main/java/org/jhipster/health/TwentyOnePointsApp.java @@ -74,8 +74,7 @@ public static void main(String[] args) { private static void logApplicationStartup(Environment env) { String protocol = Optional.ofNullable(env.getProperty("server.ssl.key-store")).map(key -> "https").orElse("http"); String serverPort = env.getProperty("server.port"); - String contextPath = Optional - .ofNullable(env.getProperty("server.servlet.context-path")) + String contextPath = Optional.ofNullable(env.getProperty("server.servlet.context-path")) .filter(StringUtils::isNotBlank) .orElse("/"); String hostAddress = "localhost"; diff --git a/src/main/java/org/jhipster/health/aop/logging/LoggingAspect.java b/src/main/java/org/jhipster/health/aop/logging/LoggingAspect.java index 40effe60..c6e2ba07 100644 --- a/src/main/java/org/jhipster/health/aop/logging/LoggingAspect.java +++ b/src/main/java/org/jhipster/health/aop/logging/LoggingAspect.java @@ -70,21 +70,19 @@ private Logger logger(JoinPoint joinPoint) { @AfterThrowing(pointcut = "applicationPackagePointcut() && springBeanPointcut()", throwing = "e") public void logAfterThrowing(JoinPoint joinPoint, Throwable e) { if (env.acceptsProfiles(Profiles.of(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT))) { - logger(joinPoint) - .error( - "Exception in {}() with cause = '{}' and exception = '{}'", - joinPoint.getSignature().getName(), - e.getCause() != null ? e.getCause() : "NULL", - e.getMessage(), - e - ); + logger(joinPoint).error( + "Exception in {}() with cause = '{}' and exception = '{}'", + joinPoint.getSignature().getName(), + e.getCause() != null ? e.getCause() : "NULL", + e.getMessage(), + e + ); } else { - logger(joinPoint) - .error( - "Exception in {}() with cause = {}", - joinPoint.getSignature().getName(), - e.getCause() != null ? e.getCause() : "NULL" - ); + logger(joinPoint).error( + "Exception in {}() with cause = {}", + joinPoint.getSignature().getName(), + e.getCause() != null ? e.getCause() : "NULL" + ); } } diff --git a/src/main/java/org/jhipster/health/config/CacheConfiguration.java b/src/main/java/org/jhipster/health/config/CacheConfiguration.java index ea48a39b..b66388db 100644 --- a/src/main/java/org/jhipster/health/config/CacheConfiguration.java +++ b/src/main/java/org/jhipster/health/config/CacheConfiguration.java @@ -26,13 +26,15 @@ public class CacheConfiguration { public CacheConfiguration(JHipsterProperties jHipsterProperties) { JHipsterProperties.Cache.Ehcache ehcache = jHipsterProperties.getCache().getEhcache(); - jcacheConfiguration = - Eh107Configuration.fromEhcacheCacheConfiguration( - CacheConfigurationBuilder - .newCacheConfigurationBuilder(Object.class, Object.class, ResourcePoolsBuilder.heap(ehcache.getMaxEntries())) - .withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofSeconds(ehcache.getTimeToLiveSeconds()))) - .build() - ); + jcacheConfiguration = Eh107Configuration.fromEhcacheCacheConfiguration( + CacheConfigurationBuilder.newCacheConfigurationBuilder( + Object.class, + Object.class, + ResourcePoolsBuilder.heap(ehcache.getMaxEntries()) + ) + .withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofSeconds(ehcache.getTimeToLiveSeconds()))) + .build() + ); } @Bean diff --git a/src/main/java/org/jhipster/health/domain/User.java b/src/main/java/org/jhipster/health/domain/User.java index 465b7959..792010ea 100644 --- a/src/main/java/org/jhipster/health/domain/User.java +++ b/src/main/java/org/jhipster/health/domain/User.java @@ -16,7 +16,6 @@ import org.hibernate.annotations.Cache; import org.hibernate.annotations.CacheConcurrencyStrategy; import org.jhipster.health.config.Constants; -import org.springframework.data.elasticsearch.annotations.FieldType; /** * A user. diff --git a/src/main/java/org/jhipster/health/management/SecurityMetersService.java b/src/main/java/org/jhipster/health/management/SecurityMetersService.java index 0789a5b0..fe245b75 100644 --- a/src/main/java/org/jhipster/health/management/SecurityMetersService.java +++ b/src/main/java/org/jhipster/health/management/SecurityMetersService.java @@ -26,8 +26,7 @@ public SecurityMetersService(MeterRegistry registry) { } private Counter.Builder invalidTokensCounterForCauseBuilder(String cause) { - return Counter - .builder(INVALID_TOKENS_METER_NAME) + return Counter.builder(INVALID_TOKENS_METER_NAME) .baseUnit(INVALID_TOKENS_METER_BASE_UNIT) .description(INVALID_TOKENS_METER_DESCRIPTION) .tag(INVALID_TOKENS_METER_CAUSE_DIMENSION, cause); diff --git a/src/main/java/org/jhipster/health/repository/search/BloodPressureSearchRepository.java b/src/main/java/org/jhipster/health/repository/search/BloodPressureSearchRepository.java index 3c932fdc..4e82fb3f 100644 --- a/src/main/java/org/jhipster/health/repository/search/BloodPressureSearchRepository.java +++ b/src/main/java/org/jhipster/health/repository/search/BloodPressureSearchRepository.java @@ -5,25 +5,17 @@ import java.util.List; import java.util.List; import java.util.stream.Collectors; -import java.util.stream.Stream; -import org.elasticsearch.search.sort.SortBuilder; import org.jhipster.health.domain.BloodPressure; import org.jhipster.health.repository.BloodPressureRepository; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; -import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate; import org.springframework.data.elasticsearch.core.SearchHit; import org.springframework.data.elasticsearch.core.SearchHits; import org.springframework.data.elasticsearch.core.query.NativeSearchQuery; -import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder; import org.springframework.data.elasticsearch.core.query.Query; import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; -import org.springframework.scheduling.annotation.Async; -import org.springframework.transaction.annotation.Isolation; -import org.springframework.transaction.annotation.Propagation; -import org.springframework.transaction.annotation.Transactional; /** * Spring Data Elasticsearch repository for the {@link BloodPressure} entity. diff --git a/src/main/java/org/jhipster/health/repository/search/PointsSearchRepository.java b/src/main/java/org/jhipster/health/repository/search/PointsSearchRepository.java index a9d99d5c..c31095d6 100644 --- a/src/main/java/org/jhipster/health/repository/search/PointsSearchRepository.java +++ b/src/main/java/org/jhipster/health/repository/search/PointsSearchRepository.java @@ -5,25 +5,17 @@ import java.util.List; import java.util.List; import java.util.stream.Collectors; -import java.util.stream.Stream; -import org.elasticsearch.search.sort.SortBuilder; import org.jhipster.health.domain.Points; import org.jhipster.health.repository.PointsRepository; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; -import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate; import org.springframework.data.elasticsearch.core.SearchHit; import org.springframework.data.elasticsearch.core.SearchHits; import org.springframework.data.elasticsearch.core.query.NativeSearchQuery; -import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder; import org.springframework.data.elasticsearch.core.query.Query; import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; -import org.springframework.scheduling.annotation.Async; -import org.springframework.transaction.annotation.Isolation; -import org.springframework.transaction.annotation.Propagation; -import org.springframework.transaction.annotation.Transactional; /** * Spring Data Elasticsearch repository for the {@link Points} entity. diff --git a/src/main/java/org/jhipster/health/repository/search/PreferencesSearchRepository.java b/src/main/java/org/jhipster/health/repository/search/PreferencesSearchRepository.java index f11a9b1f..ede49fde 100644 --- a/src/main/java/org/jhipster/health/repository/search/PreferencesSearchRepository.java +++ b/src/main/java/org/jhipster/health/repository/search/PreferencesSearchRepository.java @@ -10,10 +10,6 @@ import org.springframework.data.elasticsearch.core.query.NativeSearchQuery; import org.springframework.data.elasticsearch.core.query.Query; import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; -import org.springframework.scheduling.annotation.Async; -import org.springframework.transaction.annotation.Isolation; -import org.springframework.transaction.annotation.Propagation; -import org.springframework.transaction.annotation.Transactional; /** * Spring Data Elasticsearch repository for the {@link Preferences} entity. diff --git a/src/main/java/org/jhipster/health/repository/search/WeightSearchRepository.java b/src/main/java/org/jhipster/health/repository/search/WeightSearchRepository.java index 66901bb2..568717b5 100644 --- a/src/main/java/org/jhipster/health/repository/search/WeightSearchRepository.java +++ b/src/main/java/org/jhipster/health/repository/search/WeightSearchRepository.java @@ -5,25 +5,17 @@ import java.util.List; import java.util.List; import java.util.stream.Collectors; -import java.util.stream.Stream; -import org.elasticsearch.search.sort.SortBuilder; import org.jhipster.health.domain.Weight; import org.jhipster.health.repository.WeightRepository; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; -import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate; import org.springframework.data.elasticsearch.core.SearchHit; import org.springframework.data.elasticsearch.core.SearchHits; import org.springframework.data.elasticsearch.core.query.NativeSearchQuery; -import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder; import org.springframework.data.elasticsearch.core.query.Query; import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; -import org.springframework.scheduling.annotation.Async; -import org.springframework.transaction.annotation.Isolation; -import org.springframework.transaction.annotation.Propagation; -import org.springframework.transaction.annotation.Transactional; /** * Spring Data Elasticsearch repository for the {@link Weight} entity. diff --git a/src/main/java/org/jhipster/health/security/SecurityUtils.java b/src/main/java/org/jhipster/health/security/SecurityUtils.java index cb53c270..181d9c37 100644 --- a/src/main/java/org/jhipster/health/security/SecurityUtils.java +++ b/src/main/java/org/jhipster/health/security/SecurityUtils.java @@ -45,8 +45,7 @@ private static String extractPrincipal(Authentication authentication) { */ public static Optional getCurrentUserJWT() { SecurityContext securityContext = SecurityContextHolder.getContext(); - return Optional - .ofNullable(securityContext.getAuthentication()) + return Optional.ofNullable(securityContext.getAuthentication()) .filter(authentication -> authentication.getCredentials() instanceof String) .map(authentication -> (String) authentication.getCredentials()); } diff --git a/src/main/java/org/jhipster/health/security/jwt/TokenProvider.java b/src/main/java/org/jhipster/health/security/jwt/TokenProvider.java index fdfa9b73..4b3ae7cf 100644 --- a/src/main/java/org/jhipster/health/security/jwt/TokenProvider.java +++ b/src/main/java/org/jhipster/health/security/jwt/TokenProvider.java @@ -56,8 +56,8 @@ public TokenProvider(JHipsterProperties jHipsterProperties, SecurityMetersServic key = Keys.hmacShaKeyFor(keyBytes); jwtParser = Jwts.parserBuilder().setSigningKey(key).build(); this.tokenValidityInMilliseconds = 1000 * jHipsterProperties.getSecurity().getAuthentication().getJwt().getTokenValidityInSeconds(); - this.tokenValidityInMillisecondsForRememberMe = - 1000 * jHipsterProperties.getSecurity().getAuthentication().getJwt().getTokenValidityInSecondsForRememberMe(); + this.tokenValidityInMillisecondsForRememberMe = 1000 * + jHipsterProperties.getSecurity().getAuthentication().getJwt().getTokenValidityInSecondsForRememberMe(); this.securityMetersService = securityMetersService; } @@ -73,8 +73,7 @@ public String createToken(Authentication authentication, boolean rememberMe) { validity = new Date(now + this.tokenValidityInMilliseconds); } - return Jwts - .builder() + return Jwts.builder() .setSubject(authentication.getName()) .claim(AUTHORITIES_KEY, authorities) .signWith(key, SignatureAlgorithm.HS512) @@ -85,8 +84,7 @@ public String createToken(Authentication authentication, boolean rememberMe) { public Authentication getAuthentication(String token) { Claims claims = jwtParser.parseClaimsJws(token).getBody(); - Collection authorities = Arrays - .stream(claims.get(AUTHORITIES_KEY).toString().split(",")) + Collection authorities = Arrays.stream(claims.get(AUTHORITIES_KEY).toString().split(",")) .filter(auth -> !auth.trim().isEmpty()) .map(SimpleGrantedAuthority::new) .collect(Collectors.toList()); diff --git a/src/main/java/org/jhipster/health/service/ElasticsearchIndexService.java b/src/main/java/org/jhipster/health/service/ElasticsearchIndexService.java index 19d93d12..56670987 100644 --- a/src/main/java/org/jhipster/health/service/ElasticsearchIndexService.java +++ b/src/main/java/org/jhipster/health/service/ElasticsearchIndexService.java @@ -152,8 +152,7 @@ private void reindexForClass( } if (jpaRepository.count() > 0) { // if a JHipster entity field is the owner side of a many-to-many relationship, it should be loaded manually - List relationshipGetters = Arrays - .stream(entityClass.getDeclaredFields()) + List relationshipGetters = Arrays.stream(entityClass.getDeclaredFields()) .filter(field -> field.getType().equals(Set.class)) .filter(field -> field.getAnnotation(ManyToMany.class) != null) .filter(field -> field.getAnnotation(ManyToMany.class).mappedBy().isEmpty()) diff --git a/src/main/java/org/jhipster/health/service/UserService.java b/src/main/java/org/jhipster/health/service/UserService.java index 092c505d..c7d356e9 100644 --- a/src/main/java/org/jhipster/health/service/UserService.java +++ b/src/main/java/org/jhipster/health/service/UserService.java @@ -195,8 +195,7 @@ public User createUser(AdminUserDTO userDTO) { * @return updated user. */ public Optional updateUser(AdminUserDTO userDTO) { - return Optional - .of(userRepository.findById(userDTO.getId())) + return Optional.of(userRepository.findById(userDTO.getId())) .filter(Optional::isPresent) .map(Optional::get) .map(user -> { @@ -248,8 +247,7 @@ public void deleteUser(String login) { * @param imageUrl image URL of user. */ public void updateUser(String firstName, String lastName, String email, String langKey, String imageUrl) { - SecurityUtils - .getCurrentUserLogin() + SecurityUtils.getCurrentUserLogin() .flatMap(userRepository::findOneByLogin) .ifPresent(user -> { user.setFirstName(firstName); @@ -267,8 +265,7 @@ public void updateUser(String firstName, String lastName, String email, String l @Transactional public void changePassword(String currentClearTextPassword, String newPassword) { - SecurityUtils - .getCurrentUserLogin() + SecurityUtils.getCurrentUserLogin() .flatMap(userRepository::findOneByLogin) .ifPresent(user -> { String currentEncryptedPassword = user.getPassword(); diff --git a/src/main/java/org/jhipster/health/service/mapper/UserMapper.java b/src/main/java/org/jhipster/health/service/mapper/UserMapper.java index a8a63030..1d75c34c 100644 --- a/src/main/java/org/jhipster/health/service/mapper/UserMapper.java +++ b/src/main/java/org/jhipster/health/service/mapper/UserMapper.java @@ -63,15 +63,14 @@ private Set authoritiesFromStrings(Set authoritiesAsString) { Set authorities = new HashSet<>(); if (authoritiesAsString != null) { - authorities = - authoritiesAsString - .stream() - .map(string -> { - Authority auth = new Authority(); - auth.setName(string); - return auth; - }) - .collect(Collectors.toSet()); + authorities = authoritiesAsString + .stream() + .map(string -> { + Authority auth = new Authority(); + auth.setName(string); + return auth; + }) + .collect(Collectors.toSet()); } return authorities; diff --git a/src/main/java/org/jhipster/health/web/rest/AccountResource.java b/src/main/java/org/jhipster/health/web/rest/AccountResource.java index d50d6eb8..613d0ab2 100644 --- a/src/main/java/org/jhipster/health/web/rest/AccountResource.java +++ b/src/main/java/org/jhipster/health/web/rest/AccountResource.java @@ -114,8 +114,7 @@ public AdminUserDTO getAccount() { */ @PostMapping("/account") public void saveAccount(@Valid @RequestBody AdminUserDTO userDTO) { - String userLogin = SecurityUtils - .getCurrentUserLogin() + String userLogin = SecurityUtils.getCurrentUserLogin() .orElseThrow(() -> new AccountResourceException("Current user login not found")); Optional existingUser = userRepository.findOneByEmailIgnoreCase(userDTO.getEmail()); if (existingUser.isPresent() && (!existingUser.get().getLogin().equalsIgnoreCase(userLogin))) { diff --git a/src/main/java/org/jhipster/health/web/rest/BloodPressureResource.java b/src/main/java/org/jhipster/health/web/rest/BloodPressureResource.java index 84d15a92..9b8e4fb9 100644 --- a/src/main/java/org/jhipster/health/web/rest/BloodPressureResource.java +++ b/src/main/java/org/jhipster/health/web/rest/BloodPressureResource.java @@ -7,8 +7,6 @@ import java.util.List; import java.util.Objects; import java.util.Optional; -import java.util.stream.Collectors; -import java.util.stream.StreamSupport; import javax.validation.Valid; import javax.validation.constraints.NotNull; import org.jhipster.health.domain.BloodPressure; @@ -21,7 +19,6 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; @@ -72,8 +69,7 @@ public ResponseEntity createBloodPressure(@Valid @RequestBody Blo } BloodPressure result = bloodPressureRepository.save(bloodPressure); bloodPressureSearchRepository.index(result); - return ResponseEntity - .created(new URI("/api/blood-pressures/" + result.getId())) + return ResponseEntity.created(new URI("/api/blood-pressures/" + result.getId())) .headers(HeaderUtil.createEntityCreationAlert(applicationName, true, ENTITY_NAME, result.getId().toString())) .body(result); } @@ -107,8 +103,7 @@ public ResponseEntity updateBloodPressure( BloodPressure result = bloodPressureRepository.save(bloodPressure); bloodPressureSearchRepository.index(result); - return ResponseEntity - .ok() + return ResponseEntity.ok() .headers(HeaderUtil.createEntityUpdateAlert(applicationName, true, ENTITY_NAME, bloodPressure.getId().toString())) .body(result); } @@ -216,8 +211,7 @@ public ResponseEntity deleteBloodPressure(@PathVariable Long id) { log.debug("REST request to delete BloodPressure : {}", id); bloodPressureRepository.deleteById(id); bloodPressureSearchRepository.deleteById(id); - return ResponseEntity - .noContent() + return ResponseEntity.noContent() .headers(HeaderUtil.createEntityDeletionAlert(applicationName, true, ENTITY_NAME, id.toString())) .build(); } diff --git a/src/main/java/org/jhipster/health/web/rest/ElasticsearchIndexResource.java b/src/main/java/org/jhipster/health/web/rest/ElasticsearchIndexResource.java index d0567091..fa5f42a4 100644 --- a/src/main/java/org/jhipster/health/web/rest/ElasticsearchIndexResource.java +++ b/src/main/java/org/jhipster/health/web/rest/ElasticsearchIndexResource.java @@ -56,8 +56,7 @@ public ResponseEntity reindexAll() throws URISyntaxException { public ResponseEntity reindexSelected(@RequestBody List selectedEntities) throws URISyntaxException { log.info("REST request to reindex Elasticsearch by user : {}, entities: {}", SecurityUtils.getCurrentUserLogin(), selectedEntities); elasticsearchIndexService.reindexSelected(selectedEntities, false); - return ResponseEntity - .accepted() + return ResponseEntity.accepted() .headers(HeaderUtil.createAlert(applicationName, "elasticsearch.reindex.acceptedSelected", "")) .build(); } diff --git a/src/main/java/org/jhipster/health/web/rest/PointsResource.java b/src/main/java/org/jhipster/health/web/rest/PointsResource.java index cd02e632..94a81875 100644 --- a/src/main/java/org/jhipster/health/web/rest/PointsResource.java +++ b/src/main/java/org/jhipster/health/web/rest/PointsResource.java @@ -7,8 +7,6 @@ import java.util.List; import java.util.Objects; import java.util.Optional; -import java.util.stream.Collectors; -import java.util.stream.StreamSupport; import javax.validation.Valid; import javax.validation.constraints.NotNull; import org.jhipster.health.domain.Points; @@ -21,7 +19,6 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; @@ -69,8 +66,7 @@ public ResponseEntity createPoints(@Valid @RequestBody Points points) th } Points result = pointsRepository.save(points); pointsSearchRepository.index(result); - return ResponseEntity - .created(new URI("/api/points/" + result.getId())) + return ResponseEntity.created(new URI("/api/points/" + result.getId())) .headers(HeaderUtil.createEntityCreationAlert(applicationName, true, ENTITY_NAME, result.getId().toString())) .body(result); } @@ -104,8 +100,7 @@ public ResponseEntity updatePoints( Points result = pointsRepository.save(points); pointsSearchRepository.index(result); - return ResponseEntity - .ok() + return ResponseEntity.ok() .headers(HeaderUtil.createEntityUpdateAlert(applicationName, true, ENTITY_NAME, points.getId().toString())) .body(result); } @@ -219,8 +214,7 @@ public ResponseEntity deletePoints(@PathVariable Long id) { log.debug("REST request to delete Points : {}", id); pointsRepository.deleteById(id); pointsSearchRepository.deleteById(id); - return ResponseEntity - .noContent() + return ResponseEntity.noContent() .headers(HeaderUtil.createEntityDeletionAlert(applicationName, true, ENTITY_NAME, id.toString())) .build(); } diff --git a/src/main/java/org/jhipster/health/web/rest/PreferencesResource.java b/src/main/java/org/jhipster/health/web/rest/PreferencesResource.java index de84342e..377f3d1b 100644 --- a/src/main/java/org/jhipster/health/web/rest/PreferencesResource.java +++ b/src/main/java/org/jhipster/health/web/rest/PreferencesResource.java @@ -63,8 +63,7 @@ public ResponseEntity createPreferences(@Valid @RequestBody Prefere } Preferences result = preferencesRepository.save(preferences); preferencesSearchRepository.index(result); - return ResponseEntity - .created(new URI("/api/preferences/" + result.getId())) + return ResponseEntity.created(new URI("/api/preferences/" + result.getId())) .headers(HeaderUtil.createEntityCreationAlert(applicationName, true, ENTITY_NAME, result.getId().toString())) .body(result); } @@ -98,8 +97,7 @@ public ResponseEntity updatePreferences( Preferences result = preferencesRepository.save(preferences); preferencesSearchRepository.index(result); - return ResponseEntity - .ok() + return ResponseEntity.ok() .headers(HeaderUtil.createEntityUpdateAlert(applicationName, true, ENTITY_NAME, preferences.getId().toString())) .body(result); } @@ -197,8 +195,7 @@ public ResponseEntity deletePreferences(@PathVariable Long id) { log.debug("REST request to delete Preferences : {}", id); preferencesRepository.deleteById(id); preferencesSearchRepository.deleteById(id); - return ResponseEntity - .noContent() + return ResponseEntity.noContent() .headers(HeaderUtil.createEntityDeletionAlert(applicationName, true, ENTITY_NAME, id.toString())) .build(); } diff --git a/src/main/java/org/jhipster/health/web/rest/UserResource.java b/src/main/java/org/jhipster/health/web/rest/UserResource.java index e61652e1..389e50d1 100644 --- a/src/main/java/org/jhipster/health/web/rest/UserResource.java +++ b/src/main/java/org/jhipster/health/web/rest/UserResource.java @@ -6,8 +6,6 @@ import java.net.URISyntaxException; import java.util.*; import java.util.Collections; -import java.util.stream.Collectors; -import java.util.stream.StreamSupport; import javax.validation.Valid; import javax.validation.constraints.Pattern; import org.jhipster.health.config.Constants; @@ -124,8 +122,7 @@ public ResponseEntity createUser(@Valid @RequestBody AdminUserDTO userDTO) } else { User newUser = userService.createUser(userDTO); mailService.sendCreationEmail(newUser); - return ResponseEntity - .created(new URI("/api/admin/users/" + newUser.getLogin())) + return ResponseEntity.created(new URI("/api/admin/users/" + newUser.getLogin())) .headers(HeaderUtil.createAlert(applicationName, "userManagement.created", newUser.getLogin())) .body(newUser); } diff --git a/src/main/java/org/jhipster/health/web/rest/WeightResource.java b/src/main/java/org/jhipster/health/web/rest/WeightResource.java index 7ebef33a..6e755140 100644 --- a/src/main/java/org/jhipster/health/web/rest/WeightResource.java +++ b/src/main/java/org/jhipster/health/web/rest/WeightResource.java @@ -7,8 +7,6 @@ import java.util.List; import java.util.Objects; import java.util.Optional; -import java.util.stream.Collectors; -import java.util.stream.StreamSupport; import javax.validation.Valid; import javax.validation.constraints.NotNull; import org.jhipster.health.domain.Weight; @@ -21,7 +19,6 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.*; @@ -69,8 +66,7 @@ public ResponseEntity createWeight(@Valid @RequestBody Weight weight) th } Weight result = weightRepository.save(weight); weightSearchRepository.index(result); - return ResponseEntity - .created(new URI("/api/weights/" + result.getId())) + return ResponseEntity.created(new URI("/api/weights/" + result.getId())) .headers(HeaderUtil.createEntityCreationAlert(applicationName, true, ENTITY_NAME, result.getId().toString())) .body(result); } @@ -104,8 +100,7 @@ public ResponseEntity updateWeight( Weight result = weightRepository.save(weight); weightSearchRepository.index(result); - return ResponseEntity - .ok() + return ResponseEntity.ok() .headers(HeaderUtil.createEntityUpdateAlert(applicationName, true, ENTITY_NAME, weight.getId().toString())) .body(result); } @@ -210,8 +205,7 @@ public ResponseEntity deleteWeight(@PathVariable Long id) { log.debug("REST request to delete Weight : {}", id); weightRepository.deleteById(id); weightSearchRepository.deleteById(id); - return ResponseEntity - .noContent() + return ResponseEntity.noContent() .headers(HeaderUtil.createEntityDeletionAlert(applicationName, true, ENTITY_NAME, id.toString())) .build(); } diff --git a/src/main/java/org/jhipster/health/web/rest/errors/ExceptionTranslator.java b/src/main/java/org/jhipster/health/web/rest/errors/ExceptionTranslator.java index e5bf1085..2d155f98 100644 --- a/src/main/java/org/jhipster/health/web/rest/errors/ExceptionTranslator.java +++ b/src/main/java/org/jhipster/health/web/rest/errors/ExceptionTranslator.java @@ -68,8 +68,7 @@ public ResponseEntity process(@Nullable ResponseEntity entity, HttpServletRequest nativeRequest = request.getNativeRequest(HttpServletRequest.class); String requestUri = nativeRequest != null ? nativeRequest.getRequestURI() : StringUtils.EMPTY; - ProblemBuilder builder = Problem - .builder() + ProblemBuilder builder = Problem.builder() .withType(Problem.DEFAULT_TYPE.equals(problem.getType()) ? ErrorConstants.DEFAULT_TYPE : problem.getType()) .withStatus(problem.getStatus()) .withTitle(problem.getTitle()) @@ -95,17 +94,17 @@ public ResponseEntity handleMethodArgumentNotValid(MethodArgumentNotVal List fieldErrors = result .getFieldErrors() .stream() - .map(f -> - new FieldErrorVM( - f.getObjectName().replaceFirst("DTO$", ""), - f.getField(), - StringUtils.isNotBlank(f.getDefaultMessage()) ? f.getDefaultMessage() : f.getCode() - ) + .map( + f -> + new FieldErrorVM( + f.getObjectName().replaceFirst("DTO$", ""), + f.getField(), + StringUtils.isNotBlank(f.getDefaultMessage()) ? f.getDefaultMessage() : f.getCode() + ) ) .collect(Collectors.toList()); - Problem problem = Problem - .builder() + Problem problem = Problem.builder() .withType(ErrorConstants.CONSTRAINT_VIOLATION_TYPE) .withTitle("Method argument not valid") .withStatus(defaultConstraintViolationStatus()) @@ -170,8 +169,7 @@ public ProblemBuilder prepare(final Throwable throwable, final StatusType status if (activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_PRODUCTION)) { if (throwable instanceof HttpMessageConversionException) { - return Problem - .builder() + return Problem.builder() .withType(type) .withTitle(status.getReasonPhrase()) .withStatus(status) @@ -181,8 +179,7 @@ public ProblemBuilder prepare(final Throwable throwable, final StatusType status ); } if (throwable instanceof DataAccessException) { - return Problem - .builder() + return Problem.builder() .withType(type) .withTitle(status.getReasonPhrase()) .withStatus(status) @@ -192,8 +189,7 @@ public ProblemBuilder prepare(final Throwable throwable, final StatusType status ); } if (containsPackageName(throwable.getMessage())) { - return Problem - .builder() + return Problem.builder() .withType(type) .withTitle(status.getReasonPhrase()) .withStatus(status) @@ -204,8 +200,7 @@ public ProblemBuilder prepare(final Throwable throwable, final StatusType status } } - return Problem - .builder() + return Problem.builder() .withType(type) .withTitle(status.getReasonPhrase()) .withStatus(status) diff --git a/src/main/resources/config/application.yml b/src/main/resources/config/application.yml index 20d781a7..b640d6cf 100644 --- a/src/main/resources/config/application.yml +++ b/src/main/resources/config/application.yml @@ -243,4 +243,4 @@ jhipster: # application: # elasticsearch: -# reindex-on-startup: true \ No newline at end of file +# reindex-on-startup: true diff --git a/src/main/resources/templates/error.html b/src/main/resources/templates/error.html index 690e8560..31ee9d7d 100644 --- a/src/main/resources/templates/error.html +++ b/src/main/resources/templates/error.html @@ -1,4 +1,4 @@ - + + JHipster activation diff --git a/src/main/resources/templates/mail/creationEmail.html b/src/main/resources/templates/mail/creationEmail.html index 4e528980..ab466216 100644 --- a/src/main/resources/templates/mail/creationEmail.html +++ b/src/main/resources/templates/mail/creationEmail.html @@ -1,4 +1,4 @@ - + JHipster creation diff --git a/src/main/resources/templates/mail/passwordResetEmail.html b/src/main/resources/templates/mail/passwordResetEmail.html index 290ca6dc..eb74196f 100644 --- a/src/main/resources/templates/mail/passwordResetEmail.html +++ b/src/main/resources/templates/mail/passwordResetEmail.html @@ -1,4 +1,4 @@ - + JHipster password reset diff --git a/src/main/webapp/404.html b/src/main/webapp/404.html index 7569d7e2..3202d9ac 100644 --- a/src/main/webapp/404.html +++ b/src/main/webapp/404.html @@ -1,4 +1,4 @@ - + diff --git a/src/main/webapp/app/account/activate/activate.component.spec.ts b/src/main/webapp/app/account/activate/activate.component.spec.ts index 87f6797a..4a31d0b7 100644 --- a/src/main/webapp/app/account/activate/activate.component.spec.ts +++ b/src/main/webapp/app/account/activate/activate.component.spec.ts @@ -38,7 +38,7 @@ describe('ActivateComponent', () => { tick(); expect(service.get).toHaveBeenCalledWith('ABC123'); - }) + }), )); it('should set set success to true upon successful activation', inject( @@ -51,7 +51,7 @@ describe('ActivateComponent', () => { expect(comp.error).toBe(false); expect(comp.success).toBe(true); - }) + }), )); it('should set set error to true upon activation failure', inject( @@ -64,6 +64,6 @@ describe('ActivateComponent', () => { expect(comp.error).toBe(true); expect(comp.success).toBe(false); - }) + }), )); }); diff --git a/src/main/webapp/app/account/activate/activate.component.ts b/src/main/webapp/app/account/activate/activate.component.ts index e1bd9640..8250a0b3 100644 --- a/src/main/webapp/app/account/activate/activate.component.ts +++ b/src/main/webapp/app/account/activate/activate.component.ts @@ -12,7 +12,10 @@ export class ActivateComponent implements OnInit { error = false; success = false; - constructor(private activateService: ActivateService, private route: ActivatedRoute) {} + constructor( + private activateService: ActivateService, + private route: ActivatedRoute, + ) {} ngOnInit(): void { this.route.queryParams.pipe(mergeMap(params => this.activateService.get(params.key))).subscribe({ diff --git a/src/main/webapp/app/account/activate/activate.service.spec.ts b/src/main/webapp/app/account/activate/activate.service.spec.ts index 9acfc772..e33b3bf9 100644 --- a/src/main/webapp/app/account/activate/activate.service.spec.ts +++ b/src/main/webapp/app/account/activate/activate.service.spec.ts @@ -1,8 +1,8 @@ import { TestBed } from '@angular/core/testing'; import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; -import { ActivateService } from './activate.service'; import { ApplicationConfigService } from 'app/core/config/application-config.service'; +import { ActivateService } from './activate.service'; describe('ActivateService Service', () => { let service: ActivateService; diff --git a/src/main/webapp/app/account/activate/activate.service.ts b/src/main/webapp/app/account/activate/activate.service.ts index d6969d57..9e19475b 100644 --- a/src/main/webapp/app/account/activate/activate.service.ts +++ b/src/main/webapp/app/account/activate/activate.service.ts @@ -6,7 +6,10 @@ import { ApplicationConfigService } from 'app/core/config/application-config.ser @Injectable({ providedIn: 'root' }) export class ActivateService { - constructor(private http: HttpClient, private applicationConfigService: ApplicationConfigService) {} + constructor( + private http: HttpClient, + private applicationConfigService: ApplicationConfigService, + ) {} get(key: string): Observable<{}> { return this.http.get(this.applicationConfigService.getEndpointFor('api/activate'), { diff --git a/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.spec.ts b/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.spec.ts index 7fb9c6af..50419759 100644 --- a/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.spec.ts +++ b/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.spec.ts @@ -75,7 +75,7 @@ describe('PasswordResetFinishComponent', () => { expect(service.save).toHaveBeenCalledWith('XYZPDQ', 'password'); expect(comp.success).toBe(true); - }) + }), )); it('should notify of generic error', inject( @@ -93,6 +93,6 @@ describe('PasswordResetFinishComponent', () => { expect(service.save).toHaveBeenCalledWith('XYZPDQ', 'password'); expect(comp.success).toBe(false); expect(comp.error).toBe(true); - }) + }), )); }); diff --git a/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.ts b/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.ts index cf0eaefd..11179cb0 100644 --- a/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.ts +++ b/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.ts @@ -29,7 +29,10 @@ export class PasswordResetFinishComponent implements OnInit, AfterViewInit { }), }); - constructor(private passwordResetFinishService: PasswordResetFinishService, private route: ActivatedRoute) {} + constructor( + private passwordResetFinishService: PasswordResetFinishService, + private route: ActivatedRoute, + ) {} ngOnInit(): void { this.route.queryParams.subscribe(params => { diff --git a/src/main/webapp/app/account/password-reset/finish/password-reset-finish.service.spec.ts b/src/main/webapp/app/account/password-reset/finish/password-reset-finish.service.spec.ts index fbc81570..da6a8253 100644 --- a/src/main/webapp/app/account/password-reset/finish/password-reset-finish.service.spec.ts +++ b/src/main/webapp/app/account/password-reset/finish/password-reset-finish.service.spec.ts @@ -1,8 +1,8 @@ import { TestBed } from '@angular/core/testing'; import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; -import { PasswordResetFinishService } from './password-reset-finish.service'; import { ApplicationConfigService } from 'app/core/config/application-config.service'; +import { PasswordResetFinishService } from './password-reset-finish.service'; describe('PasswordResetFinish Service', () => { let service: PasswordResetFinishService; diff --git a/src/main/webapp/app/account/password-reset/finish/password-reset-finish.service.ts b/src/main/webapp/app/account/password-reset/finish/password-reset-finish.service.ts index 1dff3243..3696e10b 100644 --- a/src/main/webapp/app/account/password-reset/finish/password-reset-finish.service.ts +++ b/src/main/webapp/app/account/password-reset/finish/password-reset-finish.service.ts @@ -6,7 +6,10 @@ import { ApplicationConfigService } from 'app/core/config/application-config.ser @Injectable({ providedIn: 'root' }) export class PasswordResetFinishService { - constructor(private http: HttpClient, private applicationConfigService: ApplicationConfigService) {} + constructor( + private http: HttpClient, + private applicationConfigService: ApplicationConfigService, + ) {} save(key: string, newPassword: string): Observable<{}> { return this.http.post(this.applicationConfigService.getEndpointFor('api/account/reset-password/finish'), { key, newPassword }); diff --git a/src/main/webapp/app/account/password-reset/init/password-reset-init.component.spec.ts b/src/main/webapp/app/account/password-reset/init/password-reset-init.component.spec.ts index b9b6ebe4..ebc6ef8b 100644 --- a/src/main/webapp/app/account/password-reset/init/password-reset-init.component.spec.ts +++ b/src/main/webapp/app/account/password-reset/init/password-reset-init.component.spec.ts @@ -50,7 +50,7 @@ describe('PasswordResetInitComponent', () => { throwError({ status: 503, data: 'something else', - }) + }), ); comp.resetRequestForm.patchValue({ email: 'user@domain.com', diff --git a/src/main/webapp/app/account/password-reset/init/password-reset-init.component.ts b/src/main/webapp/app/account/password-reset/init/password-reset-init.component.ts index ec99fcda..06060126 100644 --- a/src/main/webapp/app/account/password-reset/init/password-reset-init.component.ts +++ b/src/main/webapp/app/account/password-reset/init/password-reset-init.component.ts @@ -16,7 +16,10 @@ export class PasswordResetInitComponent implements AfterViewInit { email: ['', [Validators.required, Validators.minLength(5), Validators.maxLength(254), Validators.email]], }); - constructor(private passwordResetInitService: PasswordResetInitService, private fb: FormBuilder) {} + constructor( + private passwordResetInitService: PasswordResetInitService, + private fb: FormBuilder, + ) {} ngAfterViewInit(): void { if (this.email) { diff --git a/src/main/webapp/app/account/password-reset/init/password-reset-init.service.spec.ts b/src/main/webapp/app/account/password-reset/init/password-reset-init.service.spec.ts index bb0995e6..3a05b2d4 100644 --- a/src/main/webapp/app/account/password-reset/init/password-reset-init.service.spec.ts +++ b/src/main/webapp/app/account/password-reset/init/password-reset-init.service.spec.ts @@ -1,8 +1,8 @@ import { TestBed } from '@angular/core/testing'; import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; -import { PasswordResetInitService } from './password-reset-init.service'; import { ApplicationConfigService } from 'app/core/config/application-config.service'; +import { PasswordResetInitService } from './password-reset-init.service'; describe('PasswordResetInit Service', () => { let service: PasswordResetInitService; diff --git a/src/main/webapp/app/account/password-reset/init/password-reset-init.service.ts b/src/main/webapp/app/account/password-reset/init/password-reset-init.service.ts index 8ff05e07..31ed7e89 100644 --- a/src/main/webapp/app/account/password-reset/init/password-reset-init.service.ts +++ b/src/main/webapp/app/account/password-reset/init/password-reset-init.service.ts @@ -6,7 +6,10 @@ import { ApplicationConfigService } from 'app/core/config/application-config.ser @Injectable({ providedIn: 'root' }) export class PasswordResetInitService { - constructor(private http: HttpClient, private applicationConfigService: ApplicationConfigService) {} + constructor( + private http: HttpClient, + private applicationConfigService: ApplicationConfigService, + ) {} save(mail: string): Observable<{}> { return this.http.post(this.applicationConfigService.getEndpointFor('api/account/reset-password/init'), mail); diff --git a/src/main/webapp/app/account/password/password-strength-bar/password-strength-bar.component.ts b/src/main/webapp/app/account/password/password-strength-bar/password-strength-bar.component.ts index 34cb8db9..700008fd 100644 --- a/src/main/webapp/app/account/password/password-strength-bar/password-strength-bar.component.ts +++ b/src/main/webapp/app/account/password/password-strength-bar/password-strength-bar.component.ts @@ -8,7 +8,10 @@ import { Component, ElementRef, Input, Renderer2 } from '@angular/core'; export class PasswordStrengthBarComponent { colors = ['#F00', '#F90', '#FF0', '#9F0', '#0F0']; - constructor(private renderer: Renderer2, private elementRef: ElementRef) {} + constructor( + private renderer: Renderer2, + private elementRef: ElementRef, + ) {} measureStrength(p: string): number { let force = 0; diff --git a/src/main/webapp/app/account/password/password.component.ts b/src/main/webapp/app/account/password/password.component.ts index bc909b28..3150753e 100644 --- a/src/main/webapp/app/account/password/password.component.ts +++ b/src/main/webapp/app/account/password/password.component.ts @@ -27,7 +27,10 @@ export class PasswordComponent implements OnInit { }), }); - constructor(private passwordService: PasswordService, private accountService: AccountService) {} + constructor( + private passwordService: PasswordService, + private accountService: AccountService, + ) {} ngOnInit(): void { this.account$ = this.accountService.identity(); diff --git a/src/main/webapp/app/account/password/password.service.spec.ts b/src/main/webapp/app/account/password/password.service.spec.ts index e0d1536f..b669e5a3 100644 --- a/src/main/webapp/app/account/password/password.service.spec.ts +++ b/src/main/webapp/app/account/password/password.service.spec.ts @@ -1,8 +1,8 @@ import { TestBed } from '@angular/core/testing'; import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; -import { PasswordService } from './password.service'; import { ApplicationConfigService } from 'app/core/config/application-config.service'; +import { PasswordService } from './password.service'; describe('Password Service', () => { let service: PasswordService; diff --git a/src/main/webapp/app/account/password/password.service.ts b/src/main/webapp/app/account/password/password.service.ts index 337a64fb..e29258f5 100644 --- a/src/main/webapp/app/account/password/password.service.ts +++ b/src/main/webapp/app/account/password/password.service.ts @@ -6,7 +6,10 @@ import { ApplicationConfigService } from 'app/core/config/application-config.ser @Injectable({ providedIn: 'root' }) export class PasswordService { - constructor(private http: HttpClient, private applicationConfigService: ApplicationConfigService) {} + constructor( + private http: HttpClient, + private applicationConfigService: ApplicationConfigService, + ) {} save(newPassword: string, currentPassword: string): Observable<{}> { return this.http.post(this.applicationConfigService.getEndpointFor('api/account/change-password'), { currentPassword, newPassword }); diff --git a/src/main/webapp/app/account/register/register.component.spec.ts b/src/main/webapp/app/account/register/register.component.spec.ts index 87019454..23ca1b0e 100644 --- a/src/main/webapp/app/account/register/register.component.spec.ts +++ b/src/main/webapp/app/account/register/register.component.spec.ts @@ -62,7 +62,7 @@ describe('RegisterComponent', () => { expect(comp.errorUserExists).toBe(false); expect(comp.errorEmailExists).toBe(false); expect(comp.error).toBe(false); - }) + }), )); it('should notify of user existence upon 400/login already in use', inject( @@ -72,7 +72,7 @@ describe('RegisterComponent', () => { throwError({ status: 400, error: { type: LOGIN_ALREADY_USED_TYPE }, - }) + }), ); comp.registerForm.patchValue({ password: 'password', @@ -85,7 +85,7 @@ describe('RegisterComponent', () => { expect(comp.errorUserExists).toBe(true); expect(comp.errorEmailExists).toBe(false); expect(comp.error).toBe(false); - }) + }), )); it('should notify of email existence upon 400/email address already in use', inject( @@ -95,7 +95,7 @@ describe('RegisterComponent', () => { throwError({ status: 400, error: { type: EMAIL_ALREADY_USED_TYPE }, - }) + }), ); comp.registerForm.patchValue({ password: 'password', @@ -108,7 +108,7 @@ describe('RegisterComponent', () => { expect(comp.errorEmailExists).toBe(true); expect(comp.errorUserExists).toBe(false); expect(comp.error).toBe(false); - }) + }), )); it('should notify of generic error', inject( @@ -117,7 +117,7 @@ describe('RegisterComponent', () => { jest.spyOn(service, 'save').mockReturnValue( throwError({ status: 503, - }) + }), ); comp.registerForm.patchValue({ password: 'password', @@ -130,6 +130,6 @@ describe('RegisterComponent', () => { expect(comp.errorUserExists).toBe(false); expect(comp.errorEmailExists).toBe(false); expect(comp.error).toBe(true); - }) + }), )); }); diff --git a/src/main/webapp/app/account/register/register.component.ts b/src/main/webapp/app/account/register/register.component.ts index 447e95ed..7d0b4d8e 100644 --- a/src/main/webapp/app/account/register/register.component.ts +++ b/src/main/webapp/app/account/register/register.component.ts @@ -44,7 +44,10 @@ export class RegisterComponent implements AfterViewInit { }), }); - constructor(private translateService: TranslateService, private registerService: RegisterService) {} + constructor( + private translateService: TranslateService, + private registerService: RegisterService, + ) {} ngAfterViewInit(): void { if (this.login) { diff --git a/src/main/webapp/app/account/register/register.model.ts b/src/main/webapp/app/account/register/register.model.ts index 17508b0d..02e04a14 100644 --- a/src/main/webapp/app/account/register/register.model.ts +++ b/src/main/webapp/app/account/register/register.model.ts @@ -1,3 +1,8 @@ export class Registration { - constructor(public login: string, public email: string, public password: string, public langKey: string) {} + constructor( + public login: string, + public email: string, + public password: string, + public langKey: string, + ) {} } diff --git a/src/main/webapp/app/account/register/register.service.spec.ts b/src/main/webapp/app/account/register/register.service.spec.ts index e0d83baf..fb9d8a73 100644 --- a/src/main/webapp/app/account/register/register.service.spec.ts +++ b/src/main/webapp/app/account/register/register.service.spec.ts @@ -1,8 +1,8 @@ import { TestBed } from '@angular/core/testing'; import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; -import { RegisterService } from './register.service'; import { ApplicationConfigService } from 'app/core/config/application-config.service'; +import { RegisterService } from './register.service'; import { Registration } from './register.model'; describe('RegisterService Service', () => { diff --git a/src/main/webapp/app/account/register/register.service.ts b/src/main/webapp/app/account/register/register.service.ts index 8696564e..29c18330 100644 --- a/src/main/webapp/app/account/register/register.service.ts +++ b/src/main/webapp/app/account/register/register.service.ts @@ -7,7 +7,10 @@ import { Registration } from './register.model'; @Injectable({ providedIn: 'root' }) export class RegisterService { - constructor(private http: HttpClient, private applicationConfigService: ApplicationConfigService) {} + constructor( + private http: HttpClient, + private applicationConfigService: ApplicationConfigService, + ) {} save(registration: Registration): Observable<{}> { return this.http.post(this.applicationConfigService.getEndpointFor('api/register'), registration); diff --git a/src/main/webapp/app/account/settings/settings.component.ts b/src/main/webapp/app/account/settings/settings.component.ts index 0e991711..efb2d255 100644 --- a/src/main/webapp/app/account/settings/settings.component.ts +++ b/src/main/webapp/app/account/settings/settings.component.ts @@ -37,7 +37,10 @@ export class SettingsComponent implements OnInit { login: new FormControl(initialAccount.login, { nonNullable: true }), }); - constructor(private accountService: AccountService, private translateService: TranslateService) {} + constructor( + private accountService: AccountService, + private translateService: TranslateService, + ) {} ngOnInit(): void { this.accountService.identity().subscribe(account => { diff --git a/src/main/webapp/app/admin/configuration/configuration.service.ts b/src/main/webapp/app/admin/configuration/configuration.service.ts index d8d30518..f03a0cce 100644 --- a/src/main/webapp/app/admin/configuration/configuration.service.ts +++ b/src/main/webapp/app/admin/configuration/configuration.service.ts @@ -8,7 +8,10 @@ import { Bean, Beans, ConfigProps, Env, PropertySource } from './configuration.m @Injectable({ providedIn: 'root' }) export class ConfigurationService { - constructor(private http: HttpClient, private applicationConfigService: ApplicationConfigService) {} + constructor( + private http: HttpClient, + private applicationConfigService: ApplicationConfigService, + ) {} getBeans(): Observable { return this.http.get(this.applicationConfigService.getEndpointFor('management/configprops')).pipe( @@ -16,9 +19,9 @@ export class ConfigurationService { Object.values( Object.values(configProps.contexts) .map(context => context.beans) - .reduce((allBeans: Beans, contextBeans: Beans) => ({ ...allBeans, ...contextBeans })) - ) - ) + .reduce((allBeans: Beans, contextBeans: Beans) => ({ ...allBeans, ...contextBeans })), + ), + ), ); } diff --git a/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex-modal.component.ts b/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex-modal.component.ts index 9996b5d9..8ed3d6fe 100644 --- a/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex-modal.component.ts +++ b/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex-modal.component.ts @@ -8,7 +8,10 @@ import { ElasticsearchReindexService } from './elasticsearch-reindex.service'; templateUrl: './elasticsearch-reindex-modal.component.html', }) export class ElasticsearchReindexModalComponent { - constructor(private elasticsearchReindexService: ElasticsearchReindexService, public activeModal: NgbActiveModal) {} + constructor( + private elasticsearchReindexService: ElasticsearchReindexService, + public activeModal: NgbActiveModal, + ) {} reindex(): void { this.elasticsearchReindexService.reindex().subscribe(() => this.activeModal.dismiss()); diff --git a/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex-selected-modal.component.ts b/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex-selected-modal.component.ts index 38649743..200a39cb 100644 --- a/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex-selected-modal.component.ts +++ b/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex-selected-modal.component.ts @@ -9,7 +9,10 @@ import { ElasticsearchReindexService } from './elasticsearch-reindex.service'; }) export class ElasticsearchReindexSelectedModalComponent { entities: string[]; - constructor(private elasticsearchReindexService: ElasticsearchReindexService, public activeModal: NgbActiveModal) { + constructor( + private elasticsearchReindexService: ElasticsearchReindexService, + public activeModal: NgbActiveModal, + ) { this.entities = []; } diff --git a/src/main/webapp/app/admin/health/health.component.ts b/src/main/webapp/app/admin/health/health.component.ts index 2d9bc2d0..c384310d 100644 --- a/src/main/webapp/app/admin/health/health.component.ts +++ b/src/main/webapp/app/admin/health/health.component.ts @@ -13,7 +13,10 @@ import { HealthModalComponent } from './modal/health-modal.component'; export class HealthComponent implements OnInit { health?: Health; - constructor(private modalService: NgbModal, private healthService: HealthService) {} + constructor( + private modalService: NgbModal, + private healthService: HealthService, + ) {} ngOnInit(): void { this.refresh(); diff --git a/src/main/webapp/app/admin/health/health.service.spec.ts b/src/main/webapp/app/admin/health/health.service.spec.ts index 850c531f..1e1ff19c 100644 --- a/src/main/webapp/app/admin/health/health.service.spec.ts +++ b/src/main/webapp/app/admin/health/health.service.spec.ts @@ -1,8 +1,8 @@ import { TestBed } from '@angular/core/testing'; import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; -import { HealthService } from './health.service'; import { ApplicationConfigService } from 'app/core/config/application-config.service'; +import { HealthService } from './health.service'; describe('HealthService Service', () => { let service: HealthService; diff --git a/src/main/webapp/app/admin/health/health.service.ts b/src/main/webapp/app/admin/health/health.service.ts index 4712a978..7a2d420d 100644 --- a/src/main/webapp/app/admin/health/health.service.ts +++ b/src/main/webapp/app/admin/health/health.service.ts @@ -7,7 +7,10 @@ import { Health } from './health.model'; @Injectable({ providedIn: 'root' }) export class HealthService { - constructor(private http: HttpClient, private applicationConfigService: ApplicationConfigService) {} + constructor( + private http: HttpClient, + private applicationConfigService: ApplicationConfigService, + ) {} checkHealth(): Observable { return this.http.get(this.applicationConfigService.getEndpointFor('management/health')); diff --git a/src/main/webapp/app/admin/logs/log.model.ts b/src/main/webapp/app/admin/logs/log.model.ts index 83f2e154..2606a885 100644 --- a/src/main/webapp/app/admin/logs/log.model.ts +++ b/src/main/webapp/app/admin/logs/log.model.ts @@ -11,5 +11,8 @@ export interface LoggersResponse { } export class Log { - constructor(public name: string, public level: Level) {} + constructor( + public name: string, + public level: Level, + ) {} } diff --git a/src/main/webapp/app/admin/logs/logs.component.html b/src/main/webapp/app/admin/logs/logs.component.html index 59ada66d..07eac547 100644 --- a/src/main/webapp/app/admin/logs/logs.component.html +++ b/src/main/webapp/app/admin/logs/logs.component.html @@ -17,7 +17,7 @@

L - {{ logger.name | slice: 0:140 }} + {{ logger.name | slice: 0 : 140 }}

[ngClass]="event?.cssClass" (mouseenter)="highlightDay.emit({ event: event })" (mouseleave)="unhighlightDay.emit({ event: event })" - [mwlCalendarTooltip]="event.title | calendarEventTitle: 'monthTooltip':event" + [mwlCalendarTooltip]="event.title | calendarEventTitle: 'monthTooltip' : event" [tooltipPlacement]="tooltipPlacement" [tooltipEvent]="event" [tooltipTemplate]="tooltipTemplate" diff --git a/src/main/webapp/app/history/history.component.ts b/src/main/webapp/app/history/history.component.ts index ca856007..168dae34 100644 --- a/src/main/webapp/app/history/history.component.ts +++ b/src/main/webapp/app/history/history.component.ts @@ -3,6 +3,9 @@ import { endOfDay, endOfMonth, format, getDaysInMonth, isSameDay, isSameMonth, s import { CalendarEvent, CalendarEventAction, CalendarEventTimesChangedEvent, CalendarMonthViewDay, CalendarView } from 'angular-calendar'; import { Router } from '@angular/router'; import { filter, Subject } from 'rxjs'; +import { EventColor } from 'calendar-utils'; +import dayjs from 'dayjs/esm'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { PointsService } from '../entities/points/service/points.service'; import { BloodPressureService } from '../entities/blood-pressure/service/blood-pressure.service'; import { WeightService } from '../entities/weight/service/weight.service'; @@ -10,11 +13,8 @@ import { PreferencesService } from '../entities/preferences/service/preferences. import { IPoints } from '../entities/points/points.model'; import { IBloodPressure } from '../entities/blood-pressure/blood-pressure.model'; import { IWeight } from '../entities/weight/weight.model'; -import { EventColor } from 'calendar-utils'; -import dayjs from 'dayjs/esm'; import { PointsDeleteDialogComponent } from '../entities/points/delete/points-delete-dialog.component'; import { ITEM_DELETED_EVENT } from '../config/navigation.constants'; -import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; import { BloodPressureDeleteDialogComponent } from '../entities/blood-pressure/delete/blood-pressure-delete-dialog.component'; import { WeightDeleteDialogComponent } from '../entities/weight/delete/weight-delete-dialog.component'; @@ -84,7 +84,7 @@ export class HistoryComponent implements OnInit { private weightService: WeightService, private preferencesService: PreferencesService, private router: Router, - protected modalService: NgbModal + protected modalService: NgbModal, ) {} setView(view: CalendarView): void { diff --git a/src/main/webapp/app/home/home.component.spec.ts b/src/main/webapp/app/home/home.component.spec.ts index 96860403..8620fa33 100644 --- a/src/main/webapp/app/home/home.component.spec.ts +++ b/src/main/webapp/app/home/home.component.spec.ts @@ -8,8 +8,8 @@ import { of, Subject } from 'rxjs'; import { AccountService } from 'app/core/auth/account.service'; import { Account } from 'app/core/auth/account.model'; -import { HomeComponent } from './home.component'; import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { HomeComponent } from './home.component'; describe('Home Component', () => { let comp: HomeComponent; diff --git a/src/main/webapp/app/home/home.component.ts b/src/main/webapp/app/home/home.component.ts index 9ea665df..cb79d9c8 100644 --- a/src/main/webapp/app/home/home.component.ts +++ b/src/main/webapp/app/home/home.component.ts @@ -5,6 +5,8 @@ import { takeUntil } from 'rxjs/operators'; import { AccountService } from 'app/core/auth/account.service'; import { Account } from 'app/core/auth/account.model'; +import { ChartConfiguration, ChartOptions } from 'chart.js'; +import dayjs from 'dayjs/esm'; import { PointsService } from '../entities/points/service/points.service'; import { IPointsPerWeek } from '../entities/points/points.model'; import { PreferencesService } from '../entities/preferences/service/preferences.service'; @@ -14,8 +16,6 @@ import { IBloodPressure, IBloodPressureByPeriod } from '../entities/blood-pressu import { WeightService } from '../entities/weight/service/weight.service'; import { IWeight, IWeightByPeriod } from '../entities/weight/weight.model'; -import { ChartConfiguration, ChartOptions } from 'chart.js'; -import dayjs from 'dayjs/esm'; @Component({ selector: 'jhi-home', @@ -42,7 +42,7 @@ export class HomeComponent implements OnInit, OnDestroy { private pointsService: PointsService, private preferencesService: PreferencesService, private bloodPressureService: BloodPressureService, - private weightService: WeightService + private weightService: WeightService, ) {} ngOnInit(): void { diff --git a/src/main/webapp/app/home/home.module.ts b/src/main/webapp/app/home/home.module.ts index 82fb265e..805a33d3 100644 --- a/src/main/webapp/app/home/home.module.ts +++ b/src/main/webapp/app/home/home.module.ts @@ -2,14 +2,14 @@ import { CUSTOM_ELEMENTS_SCHEMA, NgModule } from '@angular/core'; import { RouterModule } from '@angular/router'; import { SharedModule } from 'app/shared/shared.module'; -import { ABOUT_ROUTE, HISTORY_ROUTE, HOME_ROUTE } from './home.route'; -import { HomeComponent } from './home.component'; import { NgChartsModule } from 'ng2-charts'; import { CalendarModule, DateAdapter } from 'angular-calendar'; import { adapterFactory } from 'angular-calendar/date-adapters/date-fns'; +import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { AboutComponent } from '../about/about.component'; import { HistoryComponent } from '../history/history.component'; -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; +import { HomeComponent } from './home.component'; +import { ABOUT_ROUTE, HISTORY_ROUTE, HOME_ROUTE } from './home.route'; @NgModule({ imports: [ diff --git a/src/main/webapp/app/home/home.route.ts b/src/main/webapp/app/home/home.route.ts index 34fdf666..cc1847df 100644 --- a/src/main/webapp/app/home/home.route.ts +++ b/src/main/webapp/app/home/home.route.ts @@ -1,8 +1,8 @@ import { Route } from '@angular/router'; -import { HomeComponent } from './home.component'; import { AboutComponent } from '../about/about.component'; import { HistoryComponent } from '../history/history.component'; +import { HomeComponent } from './home.component'; export const HOME_ROUTE: Route = { path: '', diff --git a/src/main/webapp/app/layouts/error/error.component.ts b/src/main/webapp/app/layouts/error/error.component.ts index 86ba9782..db5e63d8 100644 --- a/src/main/webapp/app/layouts/error/error.component.ts +++ b/src/main/webapp/app/layouts/error/error.component.ts @@ -12,7 +12,10 @@ export class ErrorComponent implements OnInit, OnDestroy { errorKey?: string; langChangeSubscription?: Subscription; - constructor(private translateService: TranslateService, private route: ActivatedRoute) {} + constructor( + private translateService: TranslateService, + private route: ActivatedRoute, + ) {} ngOnInit(): void { this.route.data.subscribe(routeData => { diff --git a/src/main/webapp/app/layouts/main/main.component.ts b/src/main/webapp/app/layouts/main/main.component.ts index d33faeaa..ed879136 100644 --- a/src/main/webapp/app/layouts/main/main.component.ts +++ b/src/main/webapp/app/layouts/main/main.component.ts @@ -18,7 +18,7 @@ export class MainComponent implements OnInit { private titleService: Title, private router: Router, private translateService: TranslateService, - rootRenderer: RendererFactory2 + rootRenderer: RendererFactory2, ) { this.renderer = rootRenderer.createRenderer(document.querySelector('html'), null); } diff --git a/src/main/webapp/app/layouts/navbar/active-menu.directive.ts b/src/main/webapp/app/layouts/navbar/active-menu.directive.ts index 6150841e..f34ada5e 100644 --- a/src/main/webapp/app/layouts/navbar/active-menu.directive.ts +++ b/src/main/webapp/app/layouts/navbar/active-menu.directive.ts @@ -7,7 +7,11 @@ import { TranslateService, LangChangeEvent } from '@ngx-translate/core'; export class ActiveMenuDirective implements OnInit { @Input() jhiActiveMenu?: string; - constructor(private el: ElementRef, private renderer: Renderer2, private translateService: TranslateService) {} + constructor( + private el: ElementRef, + private renderer: Renderer2, + private translateService: TranslateService, + ) {} ngOnInit(): void { this.translateService.onLangChange.subscribe((event: LangChangeEvent) => { diff --git a/src/main/webapp/app/layouts/navbar/navbar.component.ts b/src/main/webapp/app/layouts/navbar/navbar.component.ts index c9c3f6dd..fa0c7800 100644 --- a/src/main/webapp/app/layouts/navbar/navbar.component.ts +++ b/src/main/webapp/app/layouts/navbar/navbar.component.ts @@ -31,7 +31,7 @@ export class NavbarComponent implements OnInit { private sessionStorageService: SessionStorageService, private accountService: AccountService, private profileService: ProfileService, - private router: Router + private router: Router, ) { if (VERSION) { this.version = VERSION.toLowerCase().startsWith('v') ? VERSION : `v${VERSION}`; diff --git a/src/main/webapp/app/layouts/profiles/profile-info.model.ts b/src/main/webapp/app/layouts/profiles/profile-info.model.ts index 8c769c74..14e920f1 100644 --- a/src/main/webapp/app/layouts/profiles/profile-info.model.ts +++ b/src/main/webapp/app/layouts/profiles/profile-info.model.ts @@ -10,6 +10,6 @@ export class ProfileInfo { public activeProfiles?: string[], public ribbonEnv?: string, public inProduction?: boolean, - public openAPIEnabled?: boolean + public openAPIEnabled?: boolean, ) {} } diff --git a/src/main/webapp/app/layouts/profiles/profile.service.ts b/src/main/webapp/app/layouts/profiles/profile.service.ts index b379d3cb..ec11dd34 100644 --- a/src/main/webapp/app/layouts/profiles/profile.service.ts +++ b/src/main/webapp/app/layouts/profiles/profile.service.ts @@ -11,7 +11,10 @@ export class ProfileService { private infoUrl = this.applicationConfigService.getEndpointFor('management/info'); private profileInfo$?: Observable; - constructor(private http: HttpClient, private applicationConfigService: ApplicationConfigService) {} + constructor( + private http: HttpClient, + private applicationConfigService: ApplicationConfigService, + ) {} getProfileInfo(): Observable { if (this.profileInfo$) { @@ -34,7 +37,7 @@ export class ProfileService { } return profileInfo; }), - shareReplay() + shareReplay(), ); return this.profileInfo$; } diff --git a/src/main/webapp/app/login/login.component.ts b/src/main/webapp/app/login/login.component.ts index 6b751acc..e8fe542d 100644 --- a/src/main/webapp/app/login/login.component.ts +++ b/src/main/webapp/app/login/login.component.ts @@ -21,7 +21,11 @@ export class LoginComponent implements OnInit, AfterViewInit { rememberMe: new FormControl(false, { nonNullable: true, validators: [Validators.required] }), }); - constructor(private accountService: AccountService, private loginService: LoginService, private router: Router) {} + constructor( + private accountService: AccountService, + private loginService: LoginService, + private router: Router, + ) {} ngOnInit(): void { // if already authenticated then navigate to home page diff --git a/src/main/webapp/app/login/login.model.ts b/src/main/webapp/app/login/login.model.ts index 422fce9a..10faab79 100644 --- a/src/main/webapp/app/login/login.model.ts +++ b/src/main/webapp/app/login/login.model.ts @@ -1,3 +1,7 @@ export class Login { - constructor(public username: string, public password: string, public rememberMe: boolean) {} + constructor( + public username: string, + public password: string, + public rememberMe: boolean, + ) {} } diff --git a/src/main/webapp/app/login/login.service.ts b/src/main/webapp/app/login/login.service.ts index bc97be6e..7b79a17b 100644 --- a/src/main/webapp/app/login/login.service.ts +++ b/src/main/webapp/app/login/login.service.ts @@ -9,7 +9,10 @@ import { Login } from './login.model'; @Injectable({ providedIn: 'root' }) export class LoginService { - constructor(private accountService: AccountService, private authServerProvider: AuthServerProvider) {} + constructor( + private accountService: AccountService, + private authServerProvider: AuthServerProvider, + ) {} login(credentials: Login): Observable { return this.authServerProvider.login(credentials).pipe(mergeMap(() => this.accountService.identity(true))); diff --git a/src/main/webapp/app/shared/alert/alert-error.component.ts b/src/main/webapp/app/shared/alert/alert-error.component.ts index 6b0e53a4..47963fd4 100644 --- a/src/main/webapp/app/shared/alert/alert-error.component.ts +++ b/src/main/webapp/app/shared/alert/alert-error.component.ts @@ -3,9 +3,9 @@ import { HttpErrorResponse } from '@angular/common/http'; import { Subscription } from 'rxjs'; import { TranslateService } from '@ngx-translate/core'; -import { AlertError } from './alert-error.model'; import { Alert, AlertService } from 'app/core/util/alert.service'; import { EventManager, EventWithContent } from 'app/core/util/event-manager.service'; +import { AlertError } from './alert-error.model'; @Component({ selector: 'jhi-alert-error', @@ -16,7 +16,11 @@ export class AlertErrorComponent implements OnDestroy { errorListener: Subscription; httpErrorListener: Subscription; - constructor(private alertService: AlertService, private eventManager: EventManager, translateService: TranslateService) { + constructor( + private alertService: AlertService, + private eventManager: EventManager, + translateService: TranslateService, + ) { this.errorListener = eventManager.subscribe('twentyOnePointsApp.error', (response: EventWithContent | string) => { const errorResponse = (response as EventWithContent).content; this.addErrorAlert(errorResponse.message, errorResponse.key, errorResponse.params); @@ -59,7 +63,7 @@ export class AlertErrorComponent implements OnDestroy { this.addErrorAlert( httpErrorResponse.error.detail ?? httpErrorResponse.error.message, httpErrorResponse.error.message, - httpErrorResponse.error.params + httpErrorResponse.error.params, ); } else { this.addErrorAlert(httpErrorResponse.error, httpErrorResponse.error); @@ -76,7 +80,7 @@ export class AlertErrorComponent implements OnDestroy { this.addErrorAlert( httpErrorResponse.error.detail ?? httpErrorResponse.error.message, httpErrorResponse.error.message, - httpErrorResponse.error.params + httpErrorResponse.error.params, ); } else { this.addErrorAlert(httpErrorResponse.error, httpErrorResponse.error); diff --git a/src/main/webapp/app/shared/alert/alert-error.model.ts b/src/main/webapp/app/shared/alert/alert-error.model.ts index 4fca767c..cc8ca7d6 100644 --- a/src/main/webapp/app/shared/alert/alert-error.model.ts +++ b/src/main/webapp/app/shared/alert/alert-error.model.ts @@ -1,3 +1,7 @@ export class AlertError { - constructor(public message: string, public key?: string, public params?: { [key: string]: unknown }) {} + constructor( + public message: string, + public key?: string, + public params?: { [key: string]: unknown }, + ) {} } diff --git a/src/main/webapp/app/shared/auth/has-any-authority.directive.ts b/src/main/webapp/app/shared/auth/has-any-authority.directive.ts index 1e47438b..af379819 100644 --- a/src/main/webapp/app/shared/auth/has-any-authority.directive.ts +++ b/src/main/webapp/app/shared/auth/has-any-authority.directive.ts @@ -23,7 +23,11 @@ export class HasAnyAuthorityDirective implements OnDestroy { private readonly destroy$ = new Subject(); - constructor(private accountService: AccountService, private templateRef: TemplateRef, private viewContainerRef: ViewContainerRef) {} + constructor( + private accountService: AccountService, + private templateRef: TemplateRef, + private viewContainerRef: ViewContainerRef, + ) {} @Input() set jhiHasAnyAuthority(value: string | string[]) { diff --git a/src/main/webapp/app/shared/filter/filter.model.ts b/src/main/webapp/app/shared/filter/filter.model.ts index a01ddaee..cce74d25 100644 --- a/src/main/webapp/app/shared/filter/filter.model.ts +++ b/src/main/webapp/app/shared/filter/filter.model.ts @@ -18,7 +18,10 @@ export interface IFilterOption { } export class FilterOption implements IFilterOption { - constructor(public name: string, public values: string[] = []) { + constructor( + public name: string, + public values: string[] = [], + ) { this.values = [...new Set(values)]; } diff --git a/src/main/webapp/app/shared/language/translate.directive.ts b/src/main/webapp/app/shared/language/translate.directive.ts index 1c3a4098..ce765ca8 100644 --- a/src/main/webapp/app/shared/language/translate.directive.ts +++ b/src/main/webapp/app/shared/language/translate.directive.ts @@ -17,7 +17,10 @@ export class TranslateDirective implements OnChanges, OnInit, OnDestroy { private readonly directiveDestroyed = new Subject(); - constructor(private el: ElementRef, private translateService: TranslateService) {} + constructor( + private el: ElementRef, + private translateService: TranslateService, + ) {} ngOnInit(): void { this.translateService.onLangChange.pipe(takeUntil(this.directiveDestroyed)).subscribe(() => { diff --git a/src/main/webapp/app/shared/language/translation.module.ts b/src/main/webapp/app/shared/language/translation.module.ts index c6fad34d..2a764061 100644 --- a/src/main/webapp/app/shared/language/translation.module.ts +++ b/src/main/webapp/app/shared/language/translation.module.ts @@ -20,7 +20,10 @@ import { SessionStorageService } from 'ngx-webstorage'; ], }) export class TranslationModule { - constructor(private translateService: TranslateService, sessionStorageService: SessionStorageService) { + constructor( + private translateService: TranslateService, + sessionStorageService: SessionStorageService, + ) { translateService.setDefaultLang('en'); // if user have changed language and navigates away from the application and back to the application then use previously choosed language const langKey = sessionStorageService.retrieve('locale') ?? 'en'; diff --git a/src/main/webapp/index.html b/src/main/webapp/index.html index a9b20eb6..aa560b76 100644 --- a/src/main/webapp/index.html +++ b/src/main/webapp/index.html @@ -1,4 +1,4 @@ - + diff --git a/src/main/webapp/swagger-ui/index.html b/src/main/webapp/swagger-ui/index.html index 3ec6f150..f7441542 100644 --- a/src/main/webapp/swagger-ui/index.html +++ b/src/main/webapp/swagger-ui/index.html @@ -1,4 +1,4 @@ - + diff --git a/src/test/java/org/jhipster/health/IntegrationTest.java b/src/test/java/org/jhipster/health/IntegrationTest.java index 04db9068..e5f66970 100644 --- a/src/test/java/org/jhipster/health/IntegrationTest.java +++ b/src/test/java/org/jhipster/health/IntegrationTest.java @@ -4,7 +4,6 @@ import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import org.jhipster.health.TwentyOnePointsApp; import org.jhipster.health.config.AsyncSyncConfiguration; import org.jhipster.health.config.EmbeddedElasticsearch; import org.jhipster.health.config.EmbeddedSQL; diff --git a/src/test/java/org/jhipster/health/config/AsyncSyncConfiguration.java b/src/test/java/org/jhipster/health/config/AsyncSyncConfiguration.java index 0a03f478..3a5bbddc 100644 --- a/src/test/java/org/jhipster/health/config/AsyncSyncConfiguration.java +++ b/src/test/java/org/jhipster/health/config/AsyncSyncConfiguration.java @@ -3,7 +3,6 @@ import java.util.concurrent.Executor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Primary; import org.springframework.core.task.SyncTaskExecutor; @Configuration diff --git a/src/test/java/org/jhipster/health/config/ElasticsearchTestContainer.java b/src/test/java/org/jhipster/health/config/ElasticsearchTestContainer.java index 6cce012d..17dc0c18 100644 --- a/src/test/java/org/jhipster/health/config/ElasticsearchTestContainer.java +++ b/src/test/java/org/jhipster/health/config/ElasticsearchTestContainer.java @@ -29,14 +29,15 @@ public void destroy() { @Override public void afterPropertiesSet() { if (null == elasticsearchContainer) { - elasticsearchContainer = - new ElasticsearchContainer(DockerImageName.parse("docker.elastic.co/elasticsearch/elasticsearch").withTag("7.17.4")) - .withStartupTimeout(Duration.of(CONTAINER_STARTUP_TIMEOUT_MINUTES, ChronoUnit.MINUTES)) - .withSharedMemorySize(256000000L) - .withEnv("ES_JAVA_OPTS", "-Xms256m -Xmx256m") - .withEnv("xpack.security.enabled", "false") - .withLogConsumer(new Slf4jLogConsumer(log)) - .withReuse(true); + elasticsearchContainer = new ElasticsearchContainer( + DockerImageName.parse("docker.elastic.co/elasticsearch/elasticsearch").withTag("7.17.4") + ) + .withStartupTimeout(Duration.of(CONTAINER_STARTUP_TIMEOUT_MINUTES, ChronoUnit.MINUTES)) + .withSharedMemorySize(256000000L) + .withEnv("ES_JAVA_OPTS", "-Xms256m -Xmx256m") + .withEnv("xpack.security.enabled", "false") + .withLogConsumer(new Slf4jLogConsumer(log)) + .withReuse(true); } if (!elasticsearchContainer.isRunning()) { elasticsearchContainer.start(); diff --git a/src/test/java/org/jhipster/health/config/PostgreSqlTestContainer.java b/src/test/java/org/jhipster/health/config/PostgreSqlTestContainer.java index 411e0044..2b218b4c 100644 --- a/src/test/java/org/jhipster/health/config/PostgreSqlTestContainer.java +++ b/src/test/java/org/jhipster/health/config/PostgreSqlTestContainer.java @@ -23,12 +23,11 @@ public void destroy() { @Override public void afterPropertiesSet() { if (null == postgreSQLContainer) { - postgreSQLContainer = - new PostgreSQLContainer<>("postgres:14.5") - .withDatabaseName("TwentyOnePoints") - .withTmpFs(Collections.singletonMap("/testtmpfs", "rw")) - .withLogConsumer(new Slf4jLogConsumer(log)) - .withReuse(true); + postgreSQLContainer = new PostgreSQLContainer<>("postgres:14.5") + .withDatabaseName("TwentyOnePoints") + .withTmpFs(Collections.singletonMap("/testtmpfs", "rw")) + .withLogConsumer(new Slf4jLogConsumer(log)) + .withReuse(true); } if (!postgreSQLContainer.isRunning()) { postgreSQLContainer.start(); diff --git a/src/test/java/org/jhipster/health/config/TestContainersSpringContextCustomizerFactory.java b/src/test/java/org/jhipster/health/config/TestContainersSpringContextCustomizerFactory.java index c0698b32..00c8b3de 100644 --- a/src/test/java/org/jhipster/health/config/TestContainersSpringContextCustomizerFactory.java +++ b/src/test/java/org/jhipster/health/config/TestContainersSpringContextCustomizerFactory.java @@ -5,8 +5,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.beans.factory.support.DefaultSingletonBeanRegistry; import org.springframework.boot.test.util.TestPropertyValues; import org.springframework.core.annotation.AnnotatedElementUtils; import org.springframework.test.context.ContextConfigurationAttributes; @@ -31,9 +29,9 @@ public ContextCustomizer createContextCustomizer(Class testClass, List testClass, List domainUserDetailsService.loadUserByUsername(USER_THREE_LOGIN)); + assertThatExceptionOfType(UserNotActivatedException.class).isThrownBy( + () -> domainUserDetailsService.loadUserByUsername(USER_THREE_LOGIN) + ); } } diff --git a/src/test/java/org/jhipster/health/security/jwt/TokenProviderSecurityMetersTests.java b/src/test/java/org/jhipster/health/security/jwt/TokenProviderSecurityMetersTests.java index 170bab3a..53ea482a 100644 --- a/src/test/java/org/jhipster/health/security/jwt/TokenProviderSecurityMetersTests.java +++ b/src/test/java/org/jhipster/health/security/jwt/TokenProviderSecurityMetersTests.java @@ -144,8 +144,7 @@ private String createTokenWithDifferentSignature() { Decoders.BASE64.decode("Xfd54a45s65fds737b9aafcb3412e07ed99b267f33413274720ddbb7f6c5e64e9f14075f2d7ed041592f0b7657baf8") ); - return Jwts - .builder() + return Jwts.builder() .setSubject("anonymous") .signWith(otherKey, SignatureAlgorithm.HS512) .setExpiration(new Date(new Date().getTime() + ONE_MINUTE)) diff --git a/src/test/java/org/jhipster/health/security/jwt/TokenProviderTest.java b/src/test/java/org/jhipster/health/security/jwt/TokenProviderTest.java index ef05678b..e62cf65c 100644 --- a/src/test/java/org/jhipster/health/security/jwt/TokenProviderTest.java +++ b/src/test/java/org/jhipster/health/security/jwt/TokenProviderTest.java @@ -131,8 +131,7 @@ private String createTokenWithDifferentSignature() { Decoders.BASE64.decode("Xfd54a45s65fds737b9aafcb3412e07ed99b267f33413274720ddbb7f6c5e64e9f14075f2d7ed041592f0b7657baf8") ); - return Jwts - .builder() + return Jwts.builder() .setSubject("anonymous") .signWith(otherKey, SignatureAlgorithm.HS512) .setExpiration(new Date(new Date().getTime() + ONE_MINUTE)) diff --git a/src/test/java/org/jhipster/health/service/MailServiceIT.java b/src/test/java/org/jhipster/health/service/MailServiceIT.java index 370c5b4a..8680d723 100644 --- a/src/test/java/org/jhipster/health/service/MailServiceIT.java +++ b/src/test/java/org/jhipster/health/service/MailServiceIT.java @@ -214,8 +214,9 @@ void testSendLocalizedEmailForAllSupportedLanguages() throws Exception { String emailTitle = (String) properties.get("email.test.title"); assertThat(message.getSubject()).isEqualTo(emailTitle); - assertThat(message.getContent().toString()) - .isEqualToNormalizingNewlines("" + emailTitle + ", http://127.0.0.1:8080, john\n"); + assertThat(message.getContent().toString()).isEqualToNormalizingNewlines( + "" + emailTitle + ", http://127.0.0.1:8080, john\n" + ); } } diff --git a/src/test/java/org/jhipster/health/service/UserServiceIT.java b/src/test/java/org/jhipster/health/service/UserServiceIT.java index b77480b2..35e9581d 100644 --- a/src/test/java/org/jhipster/health/service/UserServiceIT.java +++ b/src/test/java/org/jhipster/health/service/UserServiceIT.java @@ -13,11 +13,9 @@ import java.util.Optional; import org.apache.commons.lang3.RandomStringUtils; import org.jhipster.health.IntegrationTest; -import org.jhipster.health.config.Constants; import org.jhipster.health.domain.User; import org.jhipster.health.repository.UserRepository; import org.jhipster.health.repository.search.UserSearchRepository; -import org.jhipster.health.service.dto.AdminUserDTO; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -25,8 +23,6 @@ import org.springframework.boot.test.mock.mockito.SpyBean; import org.springframework.data.auditing.AuditingHandler; import org.springframework.data.auditing.DateTimeProvider; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageRequest; import org.springframework.transaction.annotation.Transactional; import tech.jhipster.security.RandomUtil; diff --git a/src/test/java/org/jhipster/health/web/rest/AccountResourceIT.java b/src/test/java/org/jhipster/health/web/rest/AccountResourceIT.java index 5fb4c34d..8c58d81a 100644 --- a/src/test/java/org/jhipster/health/web/rest/AccountResourceIT.java +++ b/src/test/java/org/jhipster/health/web/rest/AccountResourceIT.java @@ -17,7 +17,6 @@ import org.jhipster.health.service.UserService; import org.jhipster.health.service.dto.AdminUserDTO; import org.jhipster.health.service.dto.PasswordChangeDTO; -import org.jhipster.health.service.dto.UserDTO; import org.jhipster.health.web.rest.vm.KeyAndPasswordVM; import org.jhipster.health.web.rest.vm.ManagedUserVM; import org.junit.jupiter.api.Test; diff --git a/src/test/java/org/jhipster/health/web/rest/BloodPressureResourceIT.java b/src/test/java/org/jhipster/health/web/rest/BloodPressureResourceIT.java index a4020dfe..54e2e135 100644 --- a/src/test/java/org/jhipster/health/web/rest/BloodPressureResourceIT.java +++ b/src/test/java/org/jhipster/health/web/rest/BloodPressureResourceIT.java @@ -15,7 +15,6 @@ import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Random; import java.util.concurrent.TimeUnit; @@ -39,12 +38,10 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.data.domain.PageImpl; -import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.http.MediaType; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.transaction.annotation.Transactional; /** diff --git a/src/test/java/org/jhipster/health/web/rest/PointsResourceIT.java b/src/test/java/org/jhipster/health/web/rest/PointsResourceIT.java index e4edc288..7449394e 100644 --- a/src/test/java/org/jhipster/health/web/rest/PointsResourceIT.java +++ b/src/test/java/org/jhipster/health/web/rest/PointsResourceIT.java @@ -42,7 +42,6 @@ import org.springframework.http.MediaType; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.web.servlet.MockMvc; -import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.transaction.annotation.Transactional; /** diff --git a/src/test/java/org/jhipster/health/web/rest/PreferencesResourceIT.java b/src/test/java/org/jhipster/health/web/rest/PreferencesResourceIT.java index c0f40342..81278b16 100644 --- a/src/test/java/org/jhipster/health/web/rest/PreferencesResourceIT.java +++ b/src/test/java/org/jhipster/health/web/rest/PreferencesResourceIT.java @@ -8,12 +8,10 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Random; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; -import java.util.stream.Stream; import javax.persistence.EntityManager; import org.apache.commons.collections4.IterableUtils; import org.assertj.core.util.IterableUtil; @@ -32,7 +30,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.data.domain.PageImpl; -import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.http.MediaType; import org.springframework.security.test.context.support.WithMockUser; diff --git a/src/test/java/org/jhipster/health/web/rest/errors/ExceptionTranslatorIT.java b/src/test/java/org/jhipster/health/web/rest/errors/ExceptionTranslatorIT.java index 8a9803f0..f5173087 100644 --- a/src/test/java/org/jhipster/health/web/rest/errors/ExceptionTranslatorIT.java +++ b/src/test/java/org/jhipster/health/web/rest/errors/ExceptionTranslatorIT.java @@ -10,7 +10,6 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.web.servlet.MockMvc; diff --git a/src/test/javascript/cypress/e2e/entity/blood-pressure.cy.ts b/src/test/javascript/cypress/e2e/entity/blood-pressure.cy.ts index 2e1a1e0b..57403f9a 100644 --- a/src/test/javascript/cypress/e2e/entity/blood-pressure.cy.ts +++ b/src/test/javascript/cypress/e2e/entity/blood-pressure.cy.ts @@ -95,7 +95,7 @@ describe('BloodPressure e2e test', () => { link: '; rel="last",; rel="first"', }, body: [bloodPressure], - } + }, ).as('entitiesRequestInternal'); }); diff --git a/src/test/javascript/cypress/e2e/entity/points.cy.ts b/src/test/javascript/cypress/e2e/entity/points.cy.ts index 438719a7..02247902 100644 --- a/src/test/javascript/cypress/e2e/entity/points.cy.ts +++ b/src/test/javascript/cypress/e2e/entity/points.cy.ts @@ -95,7 +95,7 @@ describe('Points e2e test', () => { link: '; rel="last",; rel="first"', }, body: [points], - } + }, ).as('entitiesRequestInternal'); }); diff --git a/src/test/javascript/cypress/e2e/entity/preferences.cy.ts b/src/test/javascript/cypress/e2e/entity/preferences.cy.ts index 09e7ab44..505cf1f7 100644 --- a/src/test/javascript/cypress/e2e/entity/preferences.cy.ts +++ b/src/test/javascript/cypress/e2e/entity/preferences.cy.ts @@ -92,7 +92,7 @@ describe('Preferences e2e test', () => { { statusCode: 200, body: [preferences], - } + }, ).as('entitiesRequestInternal'); }); diff --git a/src/test/javascript/cypress/e2e/entity/weight.cy.ts b/src/test/javascript/cypress/e2e/entity/weight.cy.ts index b47d4d1b..d7ae6f64 100644 --- a/src/test/javascript/cypress/e2e/entity/weight.cy.ts +++ b/src/test/javascript/cypress/e2e/entity/weight.cy.ts @@ -95,7 +95,7 @@ describe('Weight e2e test', () => { link: '; rel="last",; rel="first"', }, body: [weight], - } + }, ).as('entitiesRequestInternal'); }); diff --git a/src/test/javascript/cypress/support/commands.ts b/src/test/javascript/cypress/support/commands.ts index 8ca464cc..578bc1af 100644 --- a/src/test/javascript/cypress/support/commands.ts +++ b/src/test/javascript/cypress/support/commands.ts @@ -109,7 +109,7 @@ Cypress.Commands.add('login', (username: string, password: string) => { validate() { cy.authenticatedRequest({ url: '/api/account' }).its('status').should('eq', 200); }, - } + }, ); }); diff --git a/src/test/resources/templates/mail/activationEmail.html b/src/test/resources/templates/mail/activationEmail.html index 01ad64c7..6db87f68 100644 --- a/src/test/resources/templates/mail/activationEmail.html +++ b/src/test/resources/templates/mail/activationEmail.html @@ -1,4 +1,4 @@ - + JHipster activation diff --git a/src/test/resources/templates/mail/creationEmail.html b/src/test/resources/templates/mail/creationEmail.html index e96075f4..07075e0e 100644 --- a/src/test/resources/templates/mail/creationEmail.html +++ b/src/test/resources/templates/mail/creationEmail.html @@ -1,4 +1,4 @@ - + JHipster creation diff --git a/src/test/resources/templates/mail/passwordResetEmail.html b/src/test/resources/templates/mail/passwordResetEmail.html index ca7727a8..6ddc5d29 100644 --- a/src/test/resources/templates/mail/passwordResetEmail.html +++ b/src/test/resources/templates/mail/passwordResetEmail.html @@ -1,4 +1,4 @@ - + JHipster password reset diff --git a/webpack/webpack.custom.js b/webpack/webpack.custom.js index 475da9bf..5d83de4e 100644 --- a/webpack/webpack.custom.js +++ b/webpack/webpack.custom.js @@ -32,7 +32,7 @@ module.exports = async (config, options, targetOptions) => { new WebpackNotifierPlugin({ title: '21-Points Health', contentImage: path.join(__dirname, 'logo-jhipster.png'), - }) + }), ); } @@ -72,8 +72,8 @@ module.exports = async (config, options, targetOptions) => { }, { reload: targetOptions.target === 'build', // enabled for build --watch - } - ) + }, + ), ); } @@ -84,7 +84,7 @@ module.exports = async (config, options, targetOptions) => { openAnalyzer: false, // Webpack statistics in target folder reportFilename: '../stats.html', - }) + }), ); } @@ -128,11 +128,11 @@ module.exports = async (config, options, targetOptions) => { // jhipster-needle-i18n-language-webpack - JHipster will add/remove languages in this array ], }, - }) + }), ); config = merge( - config + config, // jhipster-needle-add-webpack-config - JHipster will add custom config ); From efac4203e3cfe5d83cbb0cb15348c027747a3696 Mon Sep 17 00:00:00 2001 From: Matt Raible Date: Thu, 9 May 2024 09:55:16 -0600 Subject: [PATCH 04/17] apply actual application to migration branch From e08739c27e1f6b58cd7a0ccce54e27e9193de487 Mon Sep 17 00:00:00 2001 From: Matt Raible Date: Thu, 9 May 2024 09:55:22 -0600 Subject: [PATCH 05/17] migration application generated with JHipster bundled (target) --- .browserslistrc | 18 - .devcontainer/Dockerfile | 4 +- .devcontainer/devcontainer.json | 45 +- .eslintignore | 2 + .eslintrc.json | 2 +- .gitignore | 10 +- .jhipster/BloodPressure.json | 4 + .jhipster/Points.json | 4 + .jhipster/Preferences.json | 4 + .jhipster/Weight.json | 4 + .lintstagedrc.cjs | 3 + .lintstagedrc.js | 3 - .prettierignore | 10 +- .prettierrc | 4 + .yo-rc.json | 11 +- README.md | 97 +++-- angular.json | 11 +- build.gradle | 270 ++++-------- buildSrc/build.gradle | 18 + buildSrc/gradle/libs.versions.toml | 15 + .../jhipster.code-quality-conventions.gradle | 63 +++ .../groovy/jhipster.docker-conventions.gradle | 9 +- .../jhipster.node-gradle-conventions.gradle | 63 +++ .../jhipster.spring-cache-conventions.gradle | 7 + cypress.config.ts | 9 +- gradle.properties | 34 +- gradle/libs.versions.toml | 13 + gradle/liquibase.gradle | 53 +++ gradle/profile_dev.gradle | 32 +- gradle/profile_prod.gradle | 35 +- gradle/sonar.gradle | 26 -- gradle/wrapper/gradle-wrapper.jar | Bin 59821 -> 62076 bytes gradle/wrapper/gradle-wrapper.properties | 2 +- gradle/zipkin.gradle | 3 +- gradlew | 276 ++++++++----- gradlew.bat | 15 +- npmw | 2 +- npmw.cmd | 2 +- package.json | 173 ++++---- settings.gradle | 15 +- sonar-project.properties | 61 +-- src/main/docker/app.yml | 61 ++- .../docker/central-server-config/README.md | 1 - src/main/docker/elasticsearch.yml | 25 +- src/main/docker/jhipster-control-center.yml | 3 +- src/main/docker/monitoring.yml | 10 +- src/main/docker/postgresql.yml | 11 +- src/main/docker/services.yml | 11 + src/main/docker/sonar.yml | 14 +- src/main/docker/zipkin.yml | 7 - .../jhipster/health/GeneratedByJHipster.java | 4 +- .../jhipster/health/TwentyOnePointsApp.java | 18 +- .../health/aop/logging/LoggingAspect.java | 2 +- .../health/aop/logging/package-info.java | 4 + .../health/config/ApplicationProperties.java | 25 +- .../health/config/CRLFLogConverter.java | 17 +- .../health/config/JacksonConfiguration.java | 25 +- .../health/config/LiquibaseConfiguration.java | 36 +- .../health/config/LocaleConfiguration.java | 26 -- .../health/config/SecurityConfiguration.java | 152 +++---- .../config/SecurityJwtConfiguration.java | 76 ++++ .../StaticResourcesWebConfiguration.java | 16 +- .../jhipster/health/config/WebConfigurer.java | 7 +- .../jhipster/health/config/package-info.java | 2 +- .../health/domain/AbstractAuditingEntity.java | 6 +- .../org/jhipster/health/domain/Authority.java | 62 ++- .../jhipster/health/domain/BloodPressure.java | 10 +- .../org/jhipster/health/domain/Points.java | 12 +- .../jhipster/health/domain/Preferences.java | 10 +- .../java/org/jhipster/health/domain/User.java | 10 +- .../org/jhipster/health/domain/Weight.java | 8 +- .../domain/enumeration/package-info.java | 4 + .../jhipster/health/domain/package-info.java | 2 +- .../health/management/package-info.java | 4 + .../org/jhipster/health/package-info.java | 4 + .../repository/AuthorityRepository.java | 7 +- .../repository/BloodPressureRepository.java | 8 +- .../health/repository/PointsRepository.java | 9 +- .../repository/PreferencesRepository.java | 6 +- .../health/repository/WeightRepository.java | 9 +- .../health/repository/package-info.java | 2 +- .../search/BloodPressureSearchRepository.java | 29 +- .../search/PointsSearchRepository.java | 29 +- .../search/PreferencesSearchRepository.java | 25 +- .../search/UserSearchRepository.java | 38 +- .../search/WeightSearchRepository.java | 29 +- .../repository/search/package-info.java | 2 +- .../security/DomainUserDetailsService.java | 6 +- .../health/security/SecurityUtils.java | 15 +- .../health/security/jwt/JWTConfigurer.java | 21 - .../health/security/jwt/JWTFilter.java | 47 --- .../health/security/jwt/TokenProvider.java | 124 ------ .../health/security/package-info.java | 2 +- .../service/ElasticsearchIndexService.java | 198 --------- .../jhipster/health/service/MailService.java | 24 +- .../jhipster/health/service/UserService.java | 14 +- .../health/service/dto/AdminUserDTO.java | 2 +- .../health/service/dto/package-info.java | 2 +- .../health/service/mapper/UserMapper.java | 6 +- .../health/service/mapper/package-info.java | 2 +- .../jhipster/health/service/package-info.java | 2 +- .../health/web/filter/SpaWebFilter.java | 34 ++ .../health/web/filter/package-info.java | 4 + .../health/web/rest/AccountResource.java | 19 +- .../web/rest/AuthenticateController.java | 124 ++++++ .../health/web/rest/AuthorityResource.java | 101 +++++ .../web/rest/BloodPressureResource.java | 68 +-- .../web/rest/ClientForwardController.java | 17 - .../web/rest/ElasticsearchIndexResource.java | 63 --- .../health/web/rest/PointsResource.java | 68 +-- .../health/web/rest/PreferencesResource.java | 63 +-- .../health/web/rest/PublicUserResource.java | 24 +- .../health/web/rest/UserJWTController.java | 68 --- .../health/web/rest/UserResource.java | 23 +- .../health/web/rest/WeightResource.java | 68 +-- .../rest/errors/BadRequestAlertException.java | 29 +- .../errors/ElasticsearchExceptionMapper.java | 30 ++ .../web/rest/errors/ExceptionTranslator.java | 310 +++++++------- .../rest/errors/InvalidPasswordException.java | 17 +- .../web/rest/errors/QuerySyntaxException.java | 11 + .../health/web/rest/errors/package-info.java | 4 +- .../health/web/rest/package-info.java | 2 +- .../jhipster/health/web/rest/vm/LoginVM.java | 4 +- .../health/web/rest/vm/ManagedUserVM.java | 2 +- .../health/web/rest/vm/package-info.java | 2 +- src/main/resources/banner.txt | 2 +- src/main/resources/config/application-dev.yml | 5 +- .../resources/config/application-prod.yml | 12 +- src/main/resources/config/application.yml | 95 ++--- src/main/resources/config/bootstrap-prod.yml | 6 - src/main/resources/config/bootstrap.yml | 14 - .../00000000000000_initial_schema.xml | 16 +- ...000520_added_entity_constraints_Points.xml | 7 +- ...001452_added_entity_constraints_Weight.xml | 7 +- ...added_entity_constraints_BloodPressure.xml | 7 +- ...1_added_entity_constraints_Preferences.xml | 7 +- .../liquibase/fake-data/blood_pressure.csv | 20 +- .../config/liquibase/fake-data/points.csv | 20 +- .../liquibase/fake-data/preferences.csv | 20 +- .../config/liquibase/fake-data/weight.csv | 20 +- .../resources/config/liquibase/master.xml | 6 +- src/main/resources/logback-spring.xml | 51 +-- src/main/webapp/app/account/account.module.ts | 26 -- src/main/webapp/app/account/account.route.ts | 28 +- .../account/activate/activate.component.html | 20 +- .../activate/activate.component.spec.ts | 13 +- .../account/activate/activate.component.ts | 23 +- .../app/account/activate/activate.route.ts | 10 +- .../app/account/activate/activate.service.ts | 8 +- .../password-reset-finish.component.html | 216 +++++----- .../password-reset-finish.component.spec.ts | 17 +- .../finish/password-reset-finish.component.ts | 44 +- .../finish/password-reset-finish.route.ts | 10 +- .../finish/password-reset-finish.service.ts | 8 +- .../init/password-reset-init.component.html | 120 +++--- .../password-reset-init.component.spec.ts | 17 +- .../init/password-reset-init.component.ts | 29 +- .../init/password-reset-init.route.ts | 10 +- .../init/password-reset-init.service.ts | 8 +- .../password-strength-bar.component.spec.ts | 4 +- .../password-strength-bar.component.ts | 16 +- .../account/password/password.component.html | 246 ++++++----- .../password/password.component.spec.ts | 23 +- .../account/password/password.component.ts | 34 +- .../app/account/password/password.route.ts | 10 +- .../app/account/password/password.service.ts | 8 +- .../account/register/register.component.html | 388 +++++++++--------- .../register/register.component.spec.ts | 56 +-- .../account/register/register.component.ts | 45 +- .../app/account/register/register.route.ts | 10 +- .../app/account/register/register.service.ts | 8 +- .../account/settings/settings.component.html | 292 +++++++------ .../settings/settings.component.spec.ts | 9 +- .../account/settings/settings.component.ts | 21 +- .../app/account/settings/settings.route.ts | 10 +- .../webapp/app/admin/admin-routing.module.ts | 47 --- src/main/webapp/app/admin/admin.routes.ts | 38 ++ .../configuration.component.html | 107 ++--- .../configuration.component.spec.ts | 11 +- .../configuration/configuration.component.ts | 47 ++- .../configuration/configuration.module.ts | 12 - .../configuration/configuration.route.ts | 11 - .../configuration/configuration.service.ts | 10 +- .../webapp/app/admin/docs/docs.component.scss | 4 +- .../webapp/app/admin/docs/docs.component.ts | 5 +- src/main/webapp/app/admin/docs/docs.module.ts | 12 - src/main/webapp/app/admin/docs/docs.route.ts | 11 - ...elasticsearch-reindex-modal.component.html | 18 - .../elasticsearch-reindex-modal.component.ts | 19 - ...search-reindex-selected-modal.component.ts | 22 - .../elasticsearch-reindex.component.html | 31 -- .../elasticsearch-reindex.component.ts | 41 -- .../elasticsearch-reindex.module.ts | 23 -- .../elasticsearch-reindex.route.ts | 13 - .../elasticsearch-reindex.service.ts | 16 - .../app/admin/elasticsearch-reindex/index.ts | 5 - .../app/admin/health/health.component.html | 70 ++-- .../app/admin/health/health.component.spec.ts | 5 +- .../app/admin/health/health.component.ts | 15 +- .../webapp/app/admin/health/health.module.ts | 13 - .../webapp/app/admin/health/health.route.ts | 11 - .../webapp/app/admin/health/health.service.ts | 8 +- .../health/modal/health-modal.component.html | 60 ++- .../modal/health-modal.component.spec.ts | 5 +- .../health/modal/health-modal.component.ts | 9 +- .../webapp/app/admin/logs/logs.component.html | 138 ++++--- .../app/admin/logs/logs.component.spec.ts | 15 +- .../webapp/app/admin/logs/logs.component.ts | 67 +-- src/main/webapp/app/admin/logs/logs.module.ts | 12 - src/main/webapp/app/admin/logs/logs.route.ts | 11 - .../webapp/app/admin/logs/logs.service.ts | 8 +- .../jvm-memory/jvm-memory.component.html | 45 +- .../blocks/jvm-memory/jvm-memory.component.ts | 9 +- .../jvm-threads/jvm-threads.component.ts | 7 +- .../metrics-cache.component.html | 86 ++-- .../metrics-cache/metrics-cache.component.ts | 11 +- .../metrics-datasource.component.html | 113 ++--- .../metrics-datasource.component.ts | 11 +- .../metrics-endpoints-requests.component.html | 46 ++- .../metrics-endpoints-requests.component.ts | 9 +- .../metrics-garbagecollector.component.html | 159 +++---- .../metrics-garbagecollector.component.ts | 9 +- .../metrics-modal-threads.component.html | 116 +++--- .../metrics-modal-threads.component.spec.ts | 325 +++++++++++++++ .../metrics-modal-threads.component.ts | 7 +- .../metrics-request.component.html | 58 +-- .../metrics-request.component.ts | 11 +- .../metrics-system.component.html | 100 ++--- .../metrics-system.component.ts | 9 +- .../app/admin/metrics/metrics.component.html | 80 ++-- .../admin/metrics/metrics.component.spec.ts | 115 +++++- .../app/admin/metrics/metrics.component.ts | 51 ++- .../webapp/app/admin/metrics/metrics.model.ts | 1 - .../app/admin/metrics/metrics.module.ts | 32 -- .../webapp/app/admin/metrics/metrics.route.ts | 11 - .../app/admin/metrics/metrics.service.ts | 8 +- ...er-management-delete-dialog.component.html | 42 +- ...management-delete-dialog.component.spec.ts | 5 +- ...user-management-delete-dialog.component.ts | 14 +- .../user-management-detail.component.html | 111 ++--- .../user-management-detail.component.spec.ts | 60 +-- .../user-management-detail.component.ts | 19 +- .../list/user-management.component.html | 212 +++++----- .../list/user-management.component.spec.ts | 10 +- .../list/user-management.component.ts | 68 ++- .../service/user-management.service.spec.ts | 2 +- .../service/user-management.service.ts | 16 +- .../user-management-update.component.html | 185 ++++----- .../user-management-update.component.spec.ts | 11 +- .../user-management-update.component.ts | 27 +- .../user-management/user-management.module.ts | 20 - .../user-management/user-management.route.ts | 33 +- .../webapp/app/app-page-title-strategy.ts | 18 + src/main/webapp/app/app-routing.module.ts | 43 -- src/main/webapp/app/app.component.ts | 33 ++ src/main/webapp/app/app.config.ts | 61 +++ src/main/webapp/app/app.module.ts | 58 --- src/main/webapp/app/app.routes.ts | 46 +++ .../app/config/uib-pagination.config.ts | 13 +- .../app/core/auth/account.service.spec.ts | 10 +- .../webapp/app/core/auth/account.service.ts | 35 +- .../app/core/auth/auth-jwt.service.spec.ts | 32 +- .../webapp/app/core/auth/auth-jwt.service.ts | 29 +- .../app/core/auth/state-storage.service.ts | 44 +- .../core/auth/user-route-access.service.ts | 55 ++- .../interceptor/auth-expired.interceptor.ts | 14 +- .../app/core/interceptor/auth.interceptor.ts | 14 +- .../interceptor/error-handler.interceptor.ts | 4 +- .../interceptor/notification.interceptor.ts | 4 +- .../webapp/app/core/request/request-util.ts | 10 +- .../app/core/util/alert.service.spec.ts | 2 +- .../webapp/app/core/util/alert.service.ts | 21 +- .../webapp/app/core/util/data-util.service.ts | 5 +- .../core/util/event-manager.service.spec.ts | 26 +- .../app/core/util/parse-links.service.spec.ts | 32 +- .../app/core/util/parse-links.service.ts | 48 ++- .../admin/authority/authority.model.ts | 5 + .../admin/authority/authority.routes.ts | 42 ++ .../admin/authority/authority.test-samples.ts | 22 + .../authority-delete-dialog.component.html | 30 ++ .../authority-delete-dialog.component.spec.ts | 62 +++ .../authority-delete-dialog.component.ts | 30 ++ .../detail/authority-detail.component.html | 26 ++ .../detail/authority-detail.component.spec.ts | 54 +++ .../detail/authority-detail.component.ts | 20 + .../authority/list/authority.component.html | 73 ++++ .../list/authority.component.spec.ts | 169 ++++++++ .../authority/list/authority.component.ts | 121 ++++++ .../authority-routing-resolve.service.spec.ts | 98 +++++ .../authority-routing-resolve.service.ts | 29 ++ .../service/authority.service.spec.ts | 180 ++++++++ .../authority/service/authority.service.ts | 64 +++ .../update/authority-form.service.spec.ts | 64 +++ .../update/authority-form.service.ts | 62 +++ .../update/authority-update.component.html | 58 +++ .../update/authority-update.component.spec.ts | 80 ++++ .../update/authority-update.component.ts | 73 ++++ .../blood-pressure/blood-pressure.module.ts | 13 - .../blood-pressure/blood-pressure.routes.ts | 45 ++ .../blood-pressure.test-samples.ts | 30 +- ...lood-pressure-delete-dialog.component.html | 50 +-- ...d-pressure-delete-dialog.component.spec.ts | 3 +- .../blood-pressure-delete-dialog.component.ts | 12 +- .../blood-pressure-detail.component.html | 74 ++-- .../blood-pressure-detail.component.spec.ts | 42 +- .../detail/blood-pressure-detail.component.ts | 20 +- .../list/blood-pressure.component.html | 177 ++++---- .../list/blood-pressure.component.spec.ts | 135 ++++-- .../list/blood-pressure.component.ts | 176 ++++---- ...d-pressure-routing-resolve.service.spec.ts | 31 +- .../blood-pressure-routing-resolve.service.ts | 32 +- .../route/blood-pressure-routing.module.ts | 50 --- .../service/blood-pressure.service.spec.ts | 15 +- .../service/blood-pressure.service.ts | 30 +- .../blood-pressure-form.service.spec.ts | 1 - .../blood-pressure-update.component.html | 119 +++--- .../blood-pressure-update.component.spec.ts | 13 +- .../update/blood-pressure-update.component.ts | 22 +- .../app/entities/entity-navbar-items.ts | 12 +- .../app/entities/entity-routing.module.ts | 31 -- src/main/webapp/app/entities/entity.routes.ts | 32 ++ .../points-delete-dialog.component.html | 42 +- .../points-delete-dialog.component.spec.ts | 3 +- .../delete/points-delete-dialog.component.ts | 12 +- .../detail/points-detail.component.html | 86 ++-- .../detail/points-detail.component.spec.ts | 42 +- .../points/detail/points-detail.component.ts | 20 +- .../points/list/points.component.html | 230 ++++++----- .../points/list/points.component.spec.ts | 120 ++++-- .../entities/points/list/points.component.ts | 130 +++--- .../app/entities/points/points.module.ts | 13 - ...nts-routing.module.ts => points.routes.ts} | 23 +- .../entities/points/points.test-samples.ts | 17 +- .../points-routing-resolve.service.spec.ts | 31 +- .../route/points-routing-resolve.service.ts | 32 +- .../points/service/points.service.spec.ts | 15 +- .../entities/points/service/points.service.ts | 28 +- .../points/update/points-form.service.spec.ts | 1 - .../update/points-update.component.html | 71 ++-- .../update/points-update.component.spec.ts | 13 +- .../points/update/points-update.component.ts | 22 +- .../preferences-delete-dialog.component.html | 50 +-- ...references-delete-dialog.component.spec.ts | 3 +- .../preferences-delete-dialog.component.ts | 12 +- .../detail/preferences-detail.component.html | 64 +-- .../preferences-detail.component.spec.ts | 42 +- .../detail/preferences-detail.component.ts | 20 +- .../list/preferences.component.html | 160 ++++---- .../list/preferences.component.spec.ts | 123 +++++- .../preferences/list/preferences.component.ts | 122 +++--- .../entities/preferences/preferences.model.ts | 2 +- .../preferences/preferences.module.ts | 13 - ...outing.module.ts => preferences.routes.ts} | 23 +- .../preferences/preferences.test-samples.ts | 24 +- ...references-routing-resolve.service.spec.ts | 31 +- .../preferences-routing-resolve.service.ts | 32 +- .../service/preferences.service.spec.ts | 15 +- .../service/preferences.service.ts | 24 +- .../update/preferences-form.service.spec.ts | 1 - .../update/preferences-update.component.html | 103 +++-- .../preferences-update.component.spec.ts | 13 +- .../update/preferences-update.component.ts | 22 +- .../user/service/user.service.spec.ts | 172 ++++++++ .../app/entities/user/service/user.service.ts | 67 +++ .../webapp/app/entities/user/user.model.ts | 13 +- .../app/entities/user/user.service.spec.ts | 109 ----- .../webapp/app/entities/user/user.service.ts | 48 --- .../app/entities/user/user.test-samples.ts | 19 + .../weight-delete-dialog.component.html | 42 +- .../weight-delete-dialog.component.spec.ts | 3 +- .../delete/weight-delete-dialog.component.ts | 12 +- .../detail/weight-detail.component.html | 62 +-- .../detail/weight-detail.component.spec.ts | 42 +- .../weight/detail/weight-detail.component.ts | 20 +- .../weight/list/weight.component.html | 152 +++---- .../weight/list/weight.component.spec.ts | 135 ++++-- .../entities/weight/list/weight.component.ts | 176 ++++---- .../weight-routing-resolve.service.spec.ts | 31 +- .../route/weight-routing-resolve.service.ts | 32 +- .../weight/service/weight.service.spec.ts | 15 +- .../entities/weight/service/weight.service.ts | 28 +- .../weight/update/weight-form.service.spec.ts | 1 - .../update/weight-update.component.html | 78 ++-- .../update/weight-update.component.spec.ts | 13 +- .../weight/update/weight-update.component.ts | 22 +- .../app/entities/weight/weight.module.ts | 13 - ...ght-routing.module.ts => weight.routes.ts} | 23 +- .../entities/weight/weight.test-samples.ts | 22 +- src/main/webapp/app/home/home.component.html | 46 ++- .../webapp/app/home/home.component.spec.ts | 18 +- src/main/webapp/app/home/home.component.ts | 21 +- src/main/webapp/app/home/home.module.ts | 12 - src/main/webapp/app/home/home.route.ts | 11 - .../app/layouts/error/error.component.html | 6 +- .../app/layouts/error/error.component.ts | 19 +- .../webapp/app/layouts/error/error.route.ts | 10 +- .../app/layouts/footer/footer.component.ts | 5 +- .../app/layouts/main/main.component.spec.ts | 182 ++++---- .../webapp/app/layouts/main/main.component.ts | 53 +-- .../layouts/navbar/active-menu.directive.ts | 13 +- .../app/layouts/navbar/navbar-item.model.d.ts | 7 + .../app/layouts/navbar/navbar.component.html | 343 ++++++++-------- .../app/layouts/navbar/navbar.component.scss | 6 +- .../layouts/navbar/navbar.component.spec.ts | 17 +- .../app/layouts/navbar/navbar.component.ts | 48 +-- .../webapp/app/layouts/navbar/navbar.route.ts | 9 - .../profiles/page-ribbon.component.scss | 2 +- .../profiles/page-ribbon.component.spec.ts | 5 +- .../layouts/profiles/page-ribbon.component.ts | 19 +- .../app/layouts/profiles/profile.service.ts | 10 +- .../webapp/app/login/login.component.html | 18 +- .../webapp/app/login/login.component.spec.ts | 16 +- src/main/webapp/app/login/login.component.ts | 25 +- src/main/webapp/app/login/login.module.ts | 12 - src/main/webapp/app/login/login.route.ts | 11 - src/main/webapp/app/login/login.service.ts | 8 +- .../shared/alert/alert-error.component.html | 14 +- .../alert/alert-error.component.spec.ts | 37 +- .../app/shared/alert/alert-error.component.ts | 161 ++++---- .../app/shared/alert/alert.component.html | 14 +- .../app/shared/alert/alert.component.spec.ts | 2 +- .../app/shared/alert/alert.component.ts | 14 +- .../auth/has-any-authority.directive.spec.ts | 58 +-- .../auth/has-any-authority.directive.ts | 53 +-- .../webapp/app/shared/date/duration.pipe.ts | 3 +- .../date/format-medium-date.pipe.spec.ts | 2 +- .../shared/date/format-medium-date.pipe.ts | 3 +- .../date/format-medium-datetime.pipe.spec.ts | 2 +- .../date/format-medium-datetime.pipe.ts | 3 +- src/main/webapp/app/shared/date/index.ts | 3 + .../app/shared/filter/filter.component.html | 32 +- .../app/shared/filter/filter.component.ts | 5 +- .../app/shared/filter/filter.model.spec.ts | 4 +- .../webapp/app/shared/filter/filter.model.ts | 2 +- src/main/webapp/app/shared/filter/index.ts | 2 + .../language/find-language-from-key.pipe.ts | 7 +- src/main/webapp/app/shared/language/index.ts | 2 + .../language/translate.directive.spec.ts | 6 +- .../shared/language/translate.directive.ts | 11 +- .../app/shared/language/translation.module.ts | 18 +- .../webapp/app/shared/pagination/index.ts | 1 + .../pagination/item-count.component.spec.ts | 7 +- .../shared/pagination/item-count.component.ts | 5 +- .../webapp/app/shared/shared-libs.module.ts | 12 - src/main/webapp/app/shared/shared.module.ts | 53 +-- src/main/webapp/app/shared/sort/index.ts | 4 + .../app/shared/sort/sort-by.directive.spec.ts | 94 ++--- .../app/shared/sort/sort-by.directive.ts | 54 +-- src/main/webapp/app/shared/sort/sort-state.ts | 12 + .../app/shared/sort/sort.directive.spec.ts | 39 +- .../webapp/app/shared/sort/sort.directive.ts | 45 +- .../app/shared/sort/sort.service.spec.ts | 46 +++ .../webapp/app/shared/sort/sort.service.ts | 30 +- src/main/webapp/bootstrap.ts | 8 +- src/main/webapp/content/css/loading.css | 2 +- .../content/scss/_bootstrap-variables.scss | 11 +- src/main/webapp/content/scss/global.scss | 6 +- src/main/webapp/content/scss/vendor.scss | 2 +- src/main/webapp/i18n/en/adminAuthority.json | 23 ++ .../webapp/i18n/en/elasticsearch-reindex.json | 18 - src/main/webapp/i18n/en/global.json | 7 +- src/main/webapp/i18n/en/reset.json | 2 +- src/main/webapp/i18n/fr/adminAuthority.json | 23 ++ .../webapp/i18n/fr/elasticsearch-reindex.json | 18 - src/main/webapp/i18n/fr/global.json | 6 +- src/main/webapp/i18n/fr/health.json | 1 + src/main/webapp/index.html | 15 +- src/main/webapp/polyfills.ts | 5 - src/main/webapp/swagger-ui/index.html | 70 ++-- .../org/jhipster/health/IntegrationTest.java | 5 +- .../health/TechnicalStructureTest.java | 3 +- .../health/config/CRLFLogConverterTest.java | 133 ++++++ .../ElasticsearchTestConfiguration.java | 6 +- .../config/ElasticsearchTestContainer.java | 2 +- .../config/PostgreSqlTestContainer.java | 2 +- ...tainersSpringContextCustomizerFactory.java | 67 +++ ...tainersSpringContextCustomizerFactory.java | 76 ++-- .../health/config/WebConfigurerTest.java | 2 +- .../config/timezone/HibernateTimeZoneIT.java | 64 +-- .../jhipster/health/domain/AssertUtils.java | 15 + .../health/domain/AuthorityAsserts.java | 56 +++ .../jhipster/health/domain/AuthorityTest.java | 34 ++ .../health/domain/AuthorityTestSamples.java | 18 + .../health/domain/BloodPressureAsserts.java | 69 ++++ .../health/domain/BloodPressureTest.java | 11 +- .../domain/BloodPressureTestSamples.java | 27 ++ .../jhipster/health/domain/PointsAsserts.java | 64 +++ .../jhipster/health/domain/PointsTest.java | 11 +- .../health/domain/PointsTestSamples.java | 30 ++ .../health/domain/PreferencesAsserts.java | 61 +++ .../health/domain/PreferencesTest.java | 11 +- .../health/domain/PreferencesTestSamples.java | 24 ++ .../jhipster/health/domain/WeightAsserts.java | 68 +++ .../jhipster/health/domain/WeightTest.java | 11 +- .../health/domain/WeightTestSamples.java | 22 + .../repository/timezone/DateTimeWrapper.java | 2 +- .../jwt/AuthenticationIntegrationTest.java | 33 ++ .../health/security/jwt/JWTFilterTest.java | 117 ------ .../jwt/JwtAuthenticationTestUtils.java | 105 +++++ .../security/jwt/TokenAuthenticationIT.java | 53 +++ .../TokenAuthenticationSecurityMetersIT.java | 88 ++++ .../jwt/TokenProviderSecurityMetersTests.java | 157 ------- .../security/jwt/TokenProviderTest.java | 140 ------- .../health/service/MailServiceIT.java | 14 +- .../health/service/UserServiceIT.java | 4 +- .../health/web/filter/SpaWebFilterIT.java | 88 ++++ .../health/web/rest/AccountResourceIT.java | 161 +++----- ...rIT.java => AuthenticateControllerIT.java} | 14 +- .../health/web/rest/AuthorityResourceIT.java | 195 +++++++++ .../web/rest/BloodPressureResourceIT.java | 214 +++++----- .../web/rest/ClientForwardControllerTest.java | 68 --- .../health/web/rest/PointsResourceIT.java | 209 +++++----- .../web/rest/PreferencesResourceIT.java | 199 +++++---- .../health/web/rest/PublicUserResourceIT.java | 16 +- .../jhipster/health/web/rest/TestUtil.java | 68 ++- .../health/web/rest/UserResourceIT.java | 273 ++++++------ .../health/web/rest/WeightResourceIT.java | 202 ++++----- .../ElasticsearchExceptionMapperTest.java | 27 ++ .../rest/errors/ExceptionTranslatorIT.java | 2 +- .../ExceptionTranslatorTestController.java | 8 +- src/test/javascript/cypress/.eslintrc.json | 3 +- .../cypress/e2e/account/login-page.cy.ts | 5 + .../cypress/e2e/account/password-page.cy.ts | 19 +- .../cypress/e2e/account/register-page.cy.ts | 42 +- .../e2e/account/reset-password-page.cy.ts | 3 +- .../cypress/e2e/account/settings-page.cy.ts | 82 ++-- .../e2e/administration/administration.cy.ts | 7 +- .../cypress/e2e/entity/authority.cy.ts | 153 +++++++ .../cypress/e2e/entity/blood-pressure.cy.ts | 12 +- .../cypress/e2e/entity/points.cy.ts | 16 +- .../cypress/e2e/entity/preferences.cy.ts | 7 +- .../cypress/e2e/entity/weight.cy.ts | 9 +- src/test/javascript/cypress/plugins/index.ts | 12 + .../javascript/cypress/support/account.ts | 27 ++ .../javascript/cypress/support/commands.ts | 18 +- src/test/javascript/cypress/support/entity.ts | 3 +- src/test/javascript/cypress/support/index.ts | 1 + src/test/javascript/cypress/support/navbar.ts | 21 +- src/test/javascript/cypress/tsconfig.json | 3 +- src/test/resources/META-INF/spring.factories | 4 +- .../resources/config/application-testdev.yml | 9 +- .../resources/config/application-testprod.yml | 7 +- src/test/resources/config/application.yml | 6 +- src/test/resources/config/bootstrap.yml | 0 src/test/resources/logback.xml | 17 +- src/test/resources/testcontainers.properties | 1 - tsconfig.app.json | 6 +- tsconfig.json | 13 +- tsconfig.spec.json | 1 + webpack/proxy.conf.js | 3 +- webpack/webpack.custom.js | 8 +- 551 files changed, 12402 insertions(+), 9124 deletions(-) delete mode 100644 .browserslistrc create mode 100644 .lintstagedrc.cjs delete mode 100644 .lintstagedrc.js create mode 100644 buildSrc/build.gradle create mode 100644 buildSrc/gradle/libs.versions.toml create mode 100644 buildSrc/src/main/groovy/jhipster.code-quality-conventions.gradle rename gradle/docker.gradle => buildSrc/src/main/groovy/jhipster.docker-conventions.gradle (87%) create mode 100644 buildSrc/src/main/groovy/jhipster.node-gradle-conventions.gradle create mode 100644 buildSrc/src/main/groovy/jhipster.spring-cache-conventions.gradle create mode 100644 gradle/libs.versions.toml create mode 100644 gradle/liquibase.gradle delete mode 100644 gradle/sonar.gradle delete mode 100644 src/main/docker/central-server-config/README.md create mode 100644 src/main/docker/services.yml delete mode 100644 src/main/docker/zipkin.yml create mode 100644 src/main/java/org/jhipster/health/aop/logging/package-info.java delete mode 100644 src/main/java/org/jhipster/health/config/LocaleConfiguration.java create mode 100644 src/main/java/org/jhipster/health/config/SecurityJwtConfiguration.java create mode 100644 src/main/java/org/jhipster/health/domain/enumeration/package-info.java create mode 100644 src/main/java/org/jhipster/health/management/package-info.java create mode 100644 src/main/java/org/jhipster/health/package-info.java delete mode 100644 src/main/java/org/jhipster/health/security/jwt/JWTConfigurer.java delete mode 100644 src/main/java/org/jhipster/health/security/jwt/JWTFilter.java delete mode 100644 src/main/java/org/jhipster/health/security/jwt/TokenProvider.java delete mode 100644 src/main/java/org/jhipster/health/service/ElasticsearchIndexService.java create mode 100644 src/main/java/org/jhipster/health/web/filter/SpaWebFilter.java create mode 100644 src/main/java/org/jhipster/health/web/filter/package-info.java create mode 100644 src/main/java/org/jhipster/health/web/rest/AuthenticateController.java create mode 100644 src/main/java/org/jhipster/health/web/rest/AuthorityResource.java delete mode 100644 src/main/java/org/jhipster/health/web/rest/ClientForwardController.java delete mode 100644 src/main/java/org/jhipster/health/web/rest/ElasticsearchIndexResource.java delete mode 100644 src/main/java/org/jhipster/health/web/rest/UserJWTController.java create mode 100644 src/main/java/org/jhipster/health/web/rest/errors/ElasticsearchExceptionMapper.java create mode 100644 src/main/java/org/jhipster/health/web/rest/errors/QuerySyntaxException.java delete mode 100644 src/main/resources/config/bootstrap-prod.yml delete mode 100644 src/main/resources/config/bootstrap.yml delete mode 100644 src/main/webapp/app/account/account.module.ts delete mode 100644 src/main/webapp/app/admin/admin-routing.module.ts create mode 100644 src/main/webapp/app/admin/admin.routes.ts delete mode 100644 src/main/webapp/app/admin/configuration/configuration.module.ts delete mode 100644 src/main/webapp/app/admin/configuration/configuration.route.ts delete mode 100644 src/main/webapp/app/admin/docs/docs.module.ts delete mode 100644 src/main/webapp/app/admin/docs/docs.route.ts delete mode 100644 src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex-modal.component.html delete mode 100644 src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex-modal.component.ts delete mode 100644 src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex-selected-modal.component.ts delete mode 100644 src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex.component.html delete mode 100644 src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex.component.ts delete mode 100644 src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex.module.ts delete mode 100644 src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex.route.ts delete mode 100644 src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex.service.ts delete mode 100644 src/main/webapp/app/admin/elasticsearch-reindex/index.ts delete mode 100644 src/main/webapp/app/admin/health/health.module.ts delete mode 100644 src/main/webapp/app/admin/health/health.route.ts delete mode 100644 src/main/webapp/app/admin/logs/logs.module.ts delete mode 100644 src/main/webapp/app/admin/logs/logs.route.ts create mode 100644 src/main/webapp/app/admin/metrics/blocks/metrics-modal-threads/metrics-modal-threads.component.spec.ts delete mode 100644 src/main/webapp/app/admin/metrics/metrics.module.ts delete mode 100644 src/main/webapp/app/admin/metrics/metrics.route.ts delete mode 100644 src/main/webapp/app/admin/user-management/user-management.module.ts create mode 100644 src/main/webapp/app/app-page-title-strategy.ts delete mode 100644 src/main/webapp/app/app-routing.module.ts create mode 100644 src/main/webapp/app/app.component.ts create mode 100644 src/main/webapp/app/app.config.ts delete mode 100644 src/main/webapp/app/app.module.ts create mode 100644 src/main/webapp/app/app.routes.ts create mode 100644 src/main/webapp/app/entities/admin/authority/authority.model.ts create mode 100644 src/main/webapp/app/entities/admin/authority/authority.routes.ts create mode 100644 src/main/webapp/app/entities/admin/authority/authority.test-samples.ts create mode 100644 src/main/webapp/app/entities/admin/authority/delete/authority-delete-dialog.component.html create mode 100644 src/main/webapp/app/entities/admin/authority/delete/authority-delete-dialog.component.spec.ts create mode 100644 src/main/webapp/app/entities/admin/authority/delete/authority-delete-dialog.component.ts create mode 100644 src/main/webapp/app/entities/admin/authority/detail/authority-detail.component.html create mode 100644 src/main/webapp/app/entities/admin/authority/detail/authority-detail.component.spec.ts create mode 100644 src/main/webapp/app/entities/admin/authority/detail/authority-detail.component.ts create mode 100644 src/main/webapp/app/entities/admin/authority/list/authority.component.html create mode 100644 src/main/webapp/app/entities/admin/authority/list/authority.component.spec.ts create mode 100644 src/main/webapp/app/entities/admin/authority/list/authority.component.ts create mode 100644 src/main/webapp/app/entities/admin/authority/route/authority-routing-resolve.service.spec.ts create mode 100644 src/main/webapp/app/entities/admin/authority/route/authority-routing-resolve.service.ts create mode 100644 src/main/webapp/app/entities/admin/authority/service/authority.service.spec.ts create mode 100644 src/main/webapp/app/entities/admin/authority/service/authority.service.ts create mode 100644 src/main/webapp/app/entities/admin/authority/update/authority-form.service.spec.ts create mode 100644 src/main/webapp/app/entities/admin/authority/update/authority-form.service.ts create mode 100644 src/main/webapp/app/entities/admin/authority/update/authority-update.component.html create mode 100644 src/main/webapp/app/entities/admin/authority/update/authority-update.component.spec.ts create mode 100644 src/main/webapp/app/entities/admin/authority/update/authority-update.component.ts delete mode 100644 src/main/webapp/app/entities/blood-pressure/blood-pressure.module.ts create mode 100644 src/main/webapp/app/entities/blood-pressure/blood-pressure.routes.ts delete mode 100644 src/main/webapp/app/entities/blood-pressure/route/blood-pressure-routing.module.ts delete mode 100644 src/main/webapp/app/entities/entity-routing.module.ts create mode 100644 src/main/webapp/app/entities/entity.routes.ts delete mode 100644 src/main/webapp/app/entities/points/points.module.ts rename src/main/webapp/app/entities/points/{route/points-routing.module.ts => points.routes.ts} (52%) delete mode 100644 src/main/webapp/app/entities/preferences/preferences.module.ts rename src/main/webapp/app/entities/preferences/{route/preferences-routing.module.ts => preferences.routes.ts} (50%) create mode 100644 src/main/webapp/app/entities/user/service/user.service.spec.ts create mode 100644 src/main/webapp/app/entities/user/service/user.service.ts delete mode 100644 src/main/webapp/app/entities/user/user.service.spec.ts delete mode 100644 src/main/webapp/app/entities/user/user.service.ts create mode 100644 src/main/webapp/app/entities/user/user.test-samples.ts delete mode 100644 src/main/webapp/app/entities/weight/weight.module.ts rename src/main/webapp/app/entities/weight/{route/weight-routing.module.ts => weight.routes.ts} (52%) delete mode 100644 src/main/webapp/app/home/home.module.ts delete mode 100644 src/main/webapp/app/home/home.route.ts create mode 100644 src/main/webapp/app/layouts/navbar/navbar-item.model.d.ts delete mode 100644 src/main/webapp/app/layouts/navbar/navbar.route.ts delete mode 100644 src/main/webapp/app/login/login.module.ts delete mode 100644 src/main/webapp/app/login/login.route.ts create mode 100644 src/main/webapp/app/shared/date/index.ts create mode 100644 src/main/webapp/app/shared/filter/index.ts create mode 100644 src/main/webapp/app/shared/language/index.ts create mode 100644 src/main/webapp/app/shared/pagination/index.ts delete mode 100644 src/main/webapp/app/shared/shared-libs.module.ts create mode 100644 src/main/webapp/app/shared/sort/index.ts create mode 100644 src/main/webapp/app/shared/sort/sort-state.ts create mode 100644 src/main/webapp/app/shared/sort/sort.service.spec.ts create mode 100644 src/main/webapp/i18n/en/adminAuthority.json delete mode 100644 src/main/webapp/i18n/en/elasticsearch-reindex.json create mode 100644 src/main/webapp/i18n/fr/adminAuthority.json delete mode 100644 src/main/webapp/i18n/fr/elasticsearch-reindex.json delete mode 100644 src/main/webapp/polyfills.ts create mode 100644 src/test/java/org/jhipster/health/config/CRLFLogConverterTest.java create mode 100644 src/test/java/org/jhipster/health/config/SqlTestContainersSpringContextCustomizerFactory.java create mode 100644 src/test/java/org/jhipster/health/domain/AssertUtils.java create mode 100644 src/test/java/org/jhipster/health/domain/AuthorityAsserts.java create mode 100644 src/test/java/org/jhipster/health/domain/AuthorityTest.java create mode 100644 src/test/java/org/jhipster/health/domain/AuthorityTestSamples.java create mode 100644 src/test/java/org/jhipster/health/domain/BloodPressureAsserts.java create mode 100644 src/test/java/org/jhipster/health/domain/BloodPressureTestSamples.java create mode 100644 src/test/java/org/jhipster/health/domain/PointsAsserts.java create mode 100644 src/test/java/org/jhipster/health/domain/PointsTestSamples.java create mode 100644 src/test/java/org/jhipster/health/domain/PreferencesAsserts.java create mode 100644 src/test/java/org/jhipster/health/domain/PreferencesTestSamples.java create mode 100644 src/test/java/org/jhipster/health/domain/WeightAsserts.java create mode 100644 src/test/java/org/jhipster/health/domain/WeightTestSamples.java create mode 100644 src/test/java/org/jhipster/health/security/jwt/AuthenticationIntegrationTest.java delete mode 100644 src/test/java/org/jhipster/health/security/jwt/JWTFilterTest.java create mode 100644 src/test/java/org/jhipster/health/security/jwt/JwtAuthenticationTestUtils.java create mode 100644 src/test/java/org/jhipster/health/security/jwt/TokenAuthenticationIT.java create mode 100644 src/test/java/org/jhipster/health/security/jwt/TokenAuthenticationSecurityMetersIT.java delete mode 100644 src/test/java/org/jhipster/health/security/jwt/TokenProviderSecurityMetersTests.java delete mode 100644 src/test/java/org/jhipster/health/security/jwt/TokenProviderTest.java create mode 100644 src/test/java/org/jhipster/health/web/filter/SpaWebFilterIT.java rename src/test/java/org/jhipster/health/web/rest/{UserJWTControllerIT.java => AuthenticateControllerIT.java} (90%) create mode 100644 src/test/java/org/jhipster/health/web/rest/AuthorityResourceIT.java delete mode 100644 src/test/java/org/jhipster/health/web/rest/ClientForwardControllerTest.java create mode 100644 src/test/java/org/jhipster/health/web/rest/errors/ElasticsearchExceptionMapperTest.java create mode 100644 src/test/javascript/cypress/e2e/entity/authority.cy.ts create mode 100644 src/test/javascript/cypress/support/account.ts delete mode 100644 src/test/resources/config/bootstrap.yml delete mode 100644 src/test/resources/testcontainers.properties diff --git a/.browserslistrc b/.browserslistrc deleted file mode 100644 index 0ccadaf3..00000000 --- a/.browserslistrc +++ /dev/null @@ -1,18 +0,0 @@ -# This file is used by the build system to adjust CSS and JS output to support the specified browsers below. -# For additional information regarding the format and rule options, please see: -# https://github.com/browserslist/browserslist#queries - -# For the full list of supported browsers by the Angular framework, please see: -# https://angular.io/guide/browser-support - -# You can see what browsers were selected by your queries by running: -# npx browserslist - -last 1 Chrome version -last 1 Firefox version -last 2 Edge major versions -last 2 Safari major versions -last 2 iOS major versions -Firefox ESR -not IE 9-10 # Angular support for IE 9-10 has been deprecated and will be removed as of Angular v11. To opt-in, remove the 'not' prefix on this line. -not IE 11 # Angular supports IE 11 only as an opt-in. To opt-in, remove the 'not' prefix on this line. diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 98a7ee76..a914fd25 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,7 +1,7 @@ # See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.209.6/containers/java/.devcontainer/base.Dockerfile -# [Choice] Java version (use -bullseye variants on local arm64/Apple Silicon): 11, 17, 11-bullseye, 17-bullseye, 11-buster, 17-buster -ARG VARIANT="11" +# [Choice] Java version (use -bullseye variants on local arm64/Apple Silicon): 17, 17-bullseye, 17-buster +ARG VARIANT="17" FROM mcr.microsoft.com/vscode/devcontainers/java:0-${VARIANT} # [Option] Install Maven diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index ee261a6c..5e7d3e90 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -5,33 +5,38 @@ "build": { "dockerfile": "Dockerfile", "args": { - // Update the VARIANT arg to pick a Java version: 11, 17 + // Update the VARIANT arg to pick a Java version: 17, 19 // Append -bullseye or -buster to pin to an OS version. // Use the -bullseye variants on local arm64/Apple Silicon. - "VARIANT": "11", + "VARIANT": "17-bullseye", // Options - "INSTALL_MAVEN": "false", - "INSTALL_GRADLE": "true", - "NODE_VERSION": "lts/*" + // maven and gradle wrappers are used by default, we don't need them installed globally + // "INSTALL_MAVEN": "false", + // "INSTALL_GRADLE": "true", + "NODE_VERSION": "20.12.2" } }, - // Set *default* container specific settings.json values on container create. - "settings": { - "java.home": "/docker-java-home" - }, + "customizations": { + "vscode": { + // Set *default* container specific settings.json values on container create. + "settings": { + "java.jdt.ls.java.home": "/docker-java-home" + }, - // Add the IDs of extensions you want installed when the container is created. - "extensions": [ - "angular.ng-template", - "christian-kohler.npm-intellisense", - "firsttris.vscode-jest-runner", - "ms-vscode.vscode-typescript-tslint-plugin", - "dbaeumer.vscode-eslint", - "vscjava.vscode-java-pack", - "pivotal.vscode-boot-dev-pack", - "esbenp.prettier-vscode" - ], + // Add the IDs of extensions you want installed when the container is created. + "extensions": [ + "angular.ng-template", + "christian-kohler.npm-intellisense", + "firsttris.vscode-jest-runner", + "ms-vscode.vscode-typescript-tslint-plugin", + "dbaeumer.vscode-eslint", + "vscjava.vscode-java-pack", + "pivotal.vscode-boot-dev-pack", + "esbenp.prettier-vscode" + ] + } + }, // Use 'forwardPorts' to make a list of ports inside the container available locally. "forwardPorts": [4200, 3001, 9000, 8080], diff --git a/.eslintignore b/.eslintignore index 0f4bda1a..c858f1cc 100644 --- a/.eslintignore +++ b/.eslintignore @@ -5,4 +5,6 @@ webpack/ target/ build/ node/ +coverage/ postcss.config.js +build/resources/main/static/ diff --git a/.eslintrc.json b/.eslintrc.json index d5813308..9f1260c5 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -71,13 +71,13 @@ "@typescript-eslint/no-explicit-any": "off", "@typescript-eslint/no-floating-promises": "off", "@typescript-eslint/no-non-null-assertion": "off", - "@typescript-eslint/no-parameter-properties": ["warn", { "allows": ["public", "private", "protected"] }], "@typescript-eslint/no-shadow": ["error"], "@typescript-eslint/no-unnecessary-condition": "error", "@typescript-eslint/no-unsafe-argument": "off", "@typescript-eslint/no-unsafe-assignment": "off", "@typescript-eslint/no-unsafe-call": "off", "@typescript-eslint/no-unsafe-member-access": "off", + "@typescript-eslint/no-unused-vars": "off", "@typescript-eslint/prefer-nullish-coalescing": "error", "@typescript-eslint/prefer-optional-chain": "error", "@typescript-eslint/unbound-method": "off", diff --git a/.gitignore b/.gitignore index 4887de74..d279ca6a 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,3 @@ -###################### -# Project Specific -###################### -/src/main/webapp/content/css/main.css -/build/resources/main/static/** -/src/test/javascript/coverage/ - ###################### # Node ###################### @@ -37,7 +30,6 @@ local.properties .settings/ .loadpath .factorypath -/src/main/resources/rebel.xml # External tool builders .externalToolBuilders/** @@ -87,6 +79,8 @@ out/ ###################### .gradle/ /build/ +/buildSrc/.gradle/ +/buildSrc/build/ ###################### # Package Files diff --git a/.jhipster/BloodPressure.json b/.jhipster/BloodPressure.json index 71786e34..e23ee8e0 100644 --- a/.jhipster/BloodPressure.json +++ b/.jhipster/BloodPressure.json @@ -1,4 +1,7 @@ { + "annotations": { + "changelogDate": "20221108001642" + }, "changelogDate": "20221108001642", "fields": [ { @@ -27,6 +30,7 @@ "otherEntityRelationshipName": "bloodPressure", "ownerSide": true, "relationshipName": "user", + "relationshipSide": "left", "relationshipType": "many-to-one" } ], diff --git a/.jhipster/Points.json b/.jhipster/Points.json index 54f837e9..1345daed 100644 --- a/.jhipster/Points.json +++ b/.jhipster/Points.json @@ -1,4 +1,7 @@ { + "annotations": { + "changelogDate": "20221108000520" + }, "changelogDate": "20221108000520", "fields": [ { @@ -35,6 +38,7 @@ "otherEntityRelationshipName": "points", "ownerSide": true, "relationshipName": "user", + "relationshipSide": "left", "relationshipType": "many-to-one" } ], diff --git a/.jhipster/Preferences.json b/.jhipster/Preferences.json index 3f28a492..6b495260 100644 --- a/.jhipster/Preferences.json +++ b/.jhipster/Preferences.json @@ -1,4 +1,7 @@ { + "annotations": { + "changelogDate": "20221108002021" + }, "changelogDate": "20221108002021", "fields": [ { @@ -26,6 +29,7 @@ "otherEntityRelationshipName": "preferences", "ownerSide": true, "relationshipName": "user", + "relationshipSide": "left", "relationshipType": "one-to-one" } ], diff --git a/.jhipster/Weight.json b/.jhipster/Weight.json index ceb61e2b..c4c5bfbc 100644 --- a/.jhipster/Weight.json +++ b/.jhipster/Weight.json @@ -1,4 +1,7 @@ { + "annotations": { + "changelogDate": "20221108001452" + }, "changelogDate": "20221108001452", "fields": [ { @@ -22,6 +25,7 @@ "otherEntityRelationshipName": "weight", "ownerSide": true, "relationshipName": "user", + "relationshipSide": "left", "relationshipType": "many-to-one" } ], diff --git a/.lintstagedrc.cjs b/.lintstagedrc.cjs new file mode 100644 index 00000000..0531990f --- /dev/null +++ b/.lintstagedrc.cjs @@ -0,0 +1,3 @@ +module.exports = { + '{,**/}*.{md,json,yml,html,cjs,mjs,js,ts,tsx,css,scss,java}': ['prettier --write'], +}; diff --git a/.lintstagedrc.js b/.lintstagedrc.js deleted file mode 100644 index 68768783..00000000 --- a/.lintstagedrc.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - '{,src/**/,webpack/}*.{md,json,yml,html,cjs,mjs,js,ts,tsx,css,scss,java}': ['prettier --write'], -}; diff --git a/.prettierignore b/.prettierignore index ab0567f5..7c28f86e 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,8 +1,12 @@ node_modules -target -build package-lock.json .git -.mvn +build/ + +# Generated by jhipster:client +build/resources/main/static/ + +# Generated by jhipster:gradle +build gradle .gradle diff --git a/.prettierrc b/.prettierrc index 6d25fbaf..8550cbf8 100644 --- a/.prettierrc +++ b/.prettierrc @@ -11,6 +11,10 @@ arrowParens: avoid # jsx and tsx rules: bracketSameLine: false +plugins: + - prettier-plugin-packagejson + - prettier-plugin-java + # java rules: overrides: - files: "*.java" diff --git a/.yo-rc.json b/.yo-rc.json index b101f78b..684256b5 100644 --- a/.yo-rc.json +++ b/.yo-rc.json @@ -3,10 +3,15 @@ "applicationType": "monolith", "authenticationType": "jwt", "baseName": "TwentyOnePoints", - "blueprints": [], + "blueprints": [ + { + "name": "generator-jhipster-migrate", + "version": "1.1.0" + } + ], "buildTool": "gradle", "cacheProvider": "ehcache", - "clientFramework": "angularX", + "clientFramework": "angular", "clientPackageManager": "npm", "clientTheme": "none", "clientThemeVariant": "", @@ -27,7 +32,7 @@ "herokuDeployType": "git", "herokuJavaVersion": "11", "jhiPrefix": "jhi", - "jhipsterVersion": "7.9.3", + "jhipsterVersion": "8.4.0", "jwtSecretKey": "YjAwZDU2ODI3NzNjZjk0NjVhY2UzNGViZmY0YjY1ZGY2MDI5MTc5Y2FlZWYxNzE5Yjg5ZjMxOGZhZTgwZjA5NTFkN2VmNDM3ZGMyZDNjZTViNDAwYTg4NmNlMmM2ZDkxZTMwMDk5YmUxNWNkZmE0YTNiNDlhMGNhZmM4OTk1NmQ=", "languages": ["en", "fr"], "lastLiquibaseTimestamp": 1667866821000, diff --git a/README.md b/README.md index 1f9439bf..7f2fa06d 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # TwentyOnePoints -This application was generated using JHipster 7.9.3, you can find documentation and help at [https://www.jhipster.tech/documentation-archive/v7.9.3](https://www.jhipster.tech/documentation-archive/v7.9.3). +This application was generated using JHipster 8.4.0, you can find documentation and help at [https://www.jhipster.tech/documentation-archive/v8.4.0](https://www.jhipster.tech/documentation-archive/v8.4.0). ## Project Structure @@ -13,7 +13,7 @@ In the project root, JHipster generates configuration files for tools like git, - `.yo-rc.json` - Yeoman configuration file JHipster configuration is stored in this file at `generator-jhipster` key. You may find `generator-jhipster-*` for specific blueprints configuration. - `.yo-resolve` (optional) - Yeoman conflict resolver - Allows to use a specific action when conflicts are found skipping prompts for files that matches a pattern. Each line should match `[pattern] [action]` with pattern been a [Minimatch](https://github.com/isaacs/minimatch#minimatch) pattern and action been one of skip (default if ommited) or force. Lines starting with `#` are considered comments and are ignored. + Allows to use a specific action when conflicts are found skipping prompts for files that matches a pattern. Each line should match `[pattern] [action]` with pattern been a [Minimatch](https://github.com/isaacs/minimatch#minimatch) pattern and action been one of skip (default if omitted) or force. Lines starting with `#` are considered comments and are ignored. - `.jhipster/*.json` - JHipster entity configuration files - `npmw` - wrapper to use locally installed npm. @@ -24,7 +24,7 @@ In the project root, JHipster generates configuration files for tools like git, Before you can build this project, you must install and configure the following dependencies on your machine: -1. [Node.js][]: We use Node to run a development web server and build the project. +1. [Node.js](https://nodejs.org/): We use Node to run a development web server and build the project. Depending on your system, you can install Node either from source or as a pre-packaged bundle. After installing Node, you should be able to run the following command to install development tools. @@ -54,7 +54,7 @@ The `npm run` command will list all of the scripts available to run for this pro JHipster ships with PWA (Progressive Web App) support, and it's turned off by default. One of the main components of a PWA is a service worker. -The service worker initialization code is disabled by default. To enable it, uncomment the following code in `src/main/webapp/app/app.module.ts`: +The service worker initialization code is disabled by default. To enable it, uncomment the following code in `src/main/webapp/app/app.config.ts`: ```typescript ServiceWorkerModule.register('ngsw-worker.js', { enabled: false }), @@ -75,7 +75,7 @@ npm install --save-dev --save-exact @types/leaflet ``` Then you would import the JS and CSS files specified in library's installation instructions so that [Webpack][] knows about them: -Edit [src/main/webapp/app/app.module.ts](src/main/webapp/app/app.module.ts) file: +Edit [src/main/webapp/app/app.config.ts](src/main/webapp/app/app.config.ts) file: ``` import 'leaflet/dist/leaflet.js'; @@ -84,7 +84,7 @@ import 'leaflet/dist/leaflet.js'; Edit [src/main/webapp/content/scss/vendor.scss](src/main/webapp/content/scss/vendor.scss) file: ``` -@import '~leaflet/dist/leaflet.css'; +@import 'leaflet/dist/leaflet.css'; ``` Note: There are still a few other things remaining to do for Leaflet that we won't detail here. @@ -106,15 +106,7 @@ will generate few files: ``` create src/main/webapp/app/my-component/my-component.component.html create src/main/webapp/app/my-component/my-component.component.ts -update src/main/webapp/app/app.module.ts -``` - -### JHipster Control Center - -JHipster Control Center can help you manage and control your application(s). You can start a local control center server (accessible on http://localhost:7419) with: - -``` -docker-compose -f src/main/docker/jhipster-control-center.yml up +update src/main/webapp/app/app.config.ts ``` ## Building for production @@ -146,14 +138,16 @@ To package your application as a war in order to deploy it to an application ser ./gradlew -Pprod -Pwar clean bootWar ``` -## Testing +### JHipster Control Center -To launch your application's tests, run: +JHipster Control Center can help you manage and control your application(s). You can start a local control center server (accessible on http://localhost:7419) with: ``` -./gradlew test integrationTest jacocoTestReport +docker compose -f src/main/docker/jhipster-control-center.yml up ``` +## Testing + ### Client tests Unit tests are run by [Jest][]. They're located in [src/test/javascript/](src/test/javascript/) and can be run with: @@ -171,42 +165,57 @@ You can execute automated [lighthouse audits][https://developers.google.com/web/ You should only run the audits when your application is packaged with the production profile. The lighthouse report is created in `build/cypress/lhreport.html` -For more information, refer to the [Running tests page][]. +### Spring Boot tests + +To launch your application's tests, run: + +``` +./gradlew test integrationTest jacocoTestReport +``` + +## Others -### Code quality +### Code quality using Sonar Sonar is used to analyse code quality. You can start a local Sonar server (accessible on http://localhost:9001) with: ``` -docker-compose -f src/main/docker/sonar.yml up -d +docker compose -f src/main/docker/sonar.yml up -d ``` -Note: we have turned off authentication in [src/main/docker/sonar.yml](src/main/docker/sonar.yml) for out of the box experience while trying out SonarQube, for real use cases turn it back on. +Note: we have turned off forced authentication redirect for UI in [src/main/docker/sonar.yml](src/main/docker/sonar.yml) for out of the box experience while trying out SonarQube, for real use cases turn it back on. You can run a Sonar analysis with using the [sonar-scanner](https://docs.sonarqube.org/display/SCAN/Analyzing+with+SonarQube+Scanner) or by using the gradle plugin. Then, run a Sonar analysis: ``` -./gradlew -Pprod clean check jacocoTestReport sonarqube +./gradlew -Pprod clean check jacocoTestReport sonarqube -Dsonar.login=admin -Dsonar.password=admin +``` + +Additionally, Instead of passing `sonar.password` and `sonar.login` as CLI arguments, these parameters can be configured from [sonar-project.properties](sonar-project.properties) as shown below: + +``` +sonar.login=admin +sonar.password=admin ``` For more information, refer to the [Code quality page][]. -## Using Docker to simplify development (optional) +### Using Docker to simplify development (optional) You can use Docker to improve your JHipster development experience. A number of docker-compose configuration are available in the [src/main/docker](src/main/docker) folder to launch required third party services. For example, to start a postgresql database in a docker container, run: ``` -docker-compose -f src/main/docker/postgresql.yml up -d +docker compose -f src/main/docker/postgresql.yml up -d ``` To stop it and remove the container, run: ``` -docker-compose -f src/main/docker/postgresql.yml down +docker compose -f src/main/docker/postgresql.yml down ``` You can also fully dockerize your application and all the services that it depends on. @@ -225,7 +234,7 @@ npm run java:docker:arm64 Then run: ``` -docker-compose -f src/main/docker/app.yml up -d +docker compose -f src/main/docker/app.yml up -d ``` When running Docker Desktop on MacOS Big Sur or later, consider enabling experimental `Use the new Virtualization framework` for better processing performance ([disk access performance is worse](https://github.com/docker/roadmap/issues/7)). @@ -236,20 +245,20 @@ For more information refer to [Using Docker and Docker-Compose][], this page als To configure CI for your project, run the ci-cd sub-generator (`jhipster ci-cd`), this will let you generate configuration files for a number of Continuous Integration systems. Consult the [Setting up Continuous Integration][] page for more information. -[jhipster homepage and latest documentation]: https://www.jhipster.tech -[jhipster 7.9.3 archive]: https://www.jhipster.tech/documentation-archive/v7.9.3 -[using jhipster in development]: https://www.jhipster.tech/documentation-archive/v7.9.3/development/ -[using docker and docker-compose]: https://www.jhipster.tech/documentation-archive/v7.9.3/docker-compose -[using jhipster in production]: https://www.jhipster.tech/documentation-archive/v7.9.3/production/ -[running tests page]: https://www.jhipster.tech/documentation-archive/v7.9.3/running-tests/ -[code quality page]: https://www.jhipster.tech/documentation-archive/v7.9.3/code-quality/ -[setting up continuous integration]: https://www.jhipster.tech/documentation-archive/v7.9.3/setting-up-ci/ -[node.js]: https://nodejs.org/ -[npm]: https://www.npmjs.com/ -[webpack]: https://webpack.github.io/ -[browsersync]: https://www.browsersync.io/ -[jest]: https://facebook.github.io/jest/ -[cypress]: https://www.cypress.io/ -[leaflet]: https://leafletjs.com/ -[definitelytyped]: https://definitelytyped.org/ -[angular cli]: https://cli.angular.io/ +[JHipster Homepage and latest documentation]: https://www.jhipster.tech +[JHipster 8.4.0 archive]: https://www.jhipster.tech/documentation-archive/v8.4.0 +[Using JHipster in development]: https://www.jhipster.tech/documentation-archive/v8.4.0/development/ +[Using Docker and Docker-Compose]: https://www.jhipster.tech/documentation-archive/v8.4.0/docker-compose +[Using JHipster in production]: https://www.jhipster.tech/documentation-archive/v8.4.0/production/ +[Running tests page]: https://www.jhipster.tech/documentation-archive/v8.4.0/running-tests/ +[Code quality page]: https://www.jhipster.tech/documentation-archive/v8.4.0/code-quality/ +[Setting up Continuous Integration]: https://www.jhipster.tech/documentation-archive/v8.4.0/setting-up-ci/ +[Node.js]: https://nodejs.org/ +[NPM]: https://www.npmjs.com/ +[Webpack]: https://webpack.github.io/ +[BrowserSync]: https://www.browsersync.io/ +[Jest]: https://facebook.github.io/jest/ +[Cypress]: https://www.cypress.io/ +[Leaflet]: https://leafletjs.com/ +[DefinitelyTyped]: https://definitelytyped.org/ +[Angular CLI]: https://cli.angular.io/ diff --git a/angular.json b/angular.json index 639051a2..e14522f9 100644 --- a/angular.json +++ b/angular.json @@ -26,7 +26,7 @@ "outputPath": "build/resources/main/static/", "index": "src/main/webapp/index.html", "main": "src/main/webapp/main.ts", - "polyfills": "src/main/webapp/polyfills.ts", + "polyfills": ["zone.js"], "tsConfig": "tsconfig.app.json", "inlineStyleLanguage": "scss", "assets": [ @@ -76,15 +76,15 @@ "serve": { "builder": "@angular-builders/custom-webpack:dev-server", "options": { - "browserTarget": "twenty-one-points:build:development", + "buildTarget": "twenty-one-points:build:development", "port": 4200 }, "configurations": { "production": { - "browserTarget": "twenty-one-points:build:production" + "buildTarget": "twenty-one-points:build:production" }, "development": { - "browserTarget": "twenty-one-points:build:development" + "buildTarget": "twenty-one-points:build:development" } }, "defaultConfiguration": "development" @@ -92,7 +92,8 @@ "test": { "builder": "@angular-builders/jest:run", "options": { - "configPath": "jest.conf.js" + "configPath": "jest.conf.js", + "tsConfig": "tsconfig.spec.json" } } } diff --git a/build.gradle b/build.gradle index 887a89bb..0c483fc9 100644 --- a/build.gradle +++ b/build.gradle @@ -1,27 +1,16 @@ -buildscript { - repositories { - gradlePluginPortal() - } - dependencies { - //jhipster-needle-gradle-buildscript-dependency - JHipster will add additional gradle build script plugins here - } -} - plugins { id "java" id "maven-publish" id "idea" id "eclipse" - id "jacoco" - id "org.springframework.boot" - id "com.google.cloud.tools.jib" id "com.gorylenko.gradle-git-properties" - id "com.github.node-gradle.node" + alias(libs.plugins.spring.boot) + id "jhipster.spring-cache-conventions" + id "jhipster.docker-conventions" + id "jhipster.code-quality-conventions" + id "jhipster.node-gradle-conventions" id "org.liquibase.gradle" - id "org.sonarqube" - id "io.spring.nohttp" - id "com.github.andygoossens.gradle-modernizer-plugin" - //jhipster-needle-gradle-plugins - JHipster will add additional gradle plugins here + // jhipster-needle-gradle-plugins - JHipster will add additional gradle plugins here } group = "org.jhipster.health" @@ -29,15 +18,33 @@ version = "0.0.1-SNAPSHOT" description = "" -sourceCompatibility=11 -targetCompatibility=11 -assert System.properties["java.specification.version"] == "11" || "12" || "13" || "14" || "15" || "16" || "17" || "18" +sourceCompatibility=17 +targetCompatibility=17 +assert System.properties["java.specification.version"] == "17" || "18" || "19" || "20" || "21" -apply from: "gradle/docker.gradle" -apply from: "gradle/sonar.gradle" -//jhipster-needle-gradle-apply-from - JHipster will add additional gradle scripts to be applied here +ext { + springProfiles = "" + if (project.hasProperty("tls")) { + springProfiles += ",tls" + } + if (project.hasProperty("e2e")) { + springProfiles += ",e2e" + } +} -if (project.hasProperty("prod") || project.hasProperty("gae")) { +repositories { + // Local maven repository is required for libraries built locally with maven like development jhipster-bom. + // mavenLocal() + mavenCentral() + // jhipster-needle-gradle-repositories - JHipster will add additional repositories +} + +apply plugin: 'io.spring.dependency-management' + +apply from: "gradle/liquibase.gradle" +// jhipster-needle-gradle-apply-from - JHipster will add additional gradle scripts to be applied here + +if (project.hasProperty("prod")) { apply from: "gradle/profile_prod.gradle" } else { apply from: "gradle/profile_dev.gradle" @@ -47,19 +54,6 @@ if (project.hasProperty("war")) { apply from: "gradle/war.gradle" } -if (project.hasProperty("gae")) { - apply plugin: 'maven' - apply plugin: 'org.springframework.boot.experimental.thin-launcher' - apply plugin: 'io.spring.dependency-management' - - dependencyManagement { - imports { - mavenBom "tech.jhipster:jhipster-dependencies:${jhipsterDependenciesVersion}" - } - } - appengineStage.dependsOn thinResolve -} - idea { module { @@ -93,59 +87,19 @@ test { // uncomment if the tests reports are not generated // see https://github.com/jhipster/generator-jhipster/pull/2771 and https://github.com/jhipster/generator-jhipster/pull/4484 // ignoreFailures true - reports.html.enabled = false + reports.html.required = false } -modernizer { - failOnViolations = true - includeTestClasses = true -} - - check.dependsOn integrationTest task testReport(type: TestReport) { - destinationDir = file("$buildDir/reports/tests") - reportOn test + destinationDirectory = file("$buildDir/reports/tests") + testResults.from(test) } task integrationTestReport(type: TestReport) { - destinationDir = file("$buildDir/reports/tests") - reportOn integrationTest -} - -if (!project.hasProperty("runList")) { - project.ext.runList = "main" -} - -project.ext.diffChangelogFile = "src/main/resources/config/liquibase/changelog/" + new Date().format("yyyyMMddHHmmss") + "_changelog.xml" - -liquibase { - activities { - main { - driver "org.h2.Driver" - url "jdbc:h2:file:./build/h2db/db/twentyonepoints" - username "TwentyOnePoints" - password "" - changeLogFile "src/main/resources/config/liquibase/master.xml" - defaultSchemaName "" - logLevel "debug" - classpath "src/main/resources/" - } - diffLog { - driver "org.h2.Driver" - url "jdbc:h2:file:./build/h2db/db/twentyonepoints" - username "TwentyOnePoints" - password "" - changeLogFile project.ext.diffChangelogFile - referenceUrl "hibernate:spring:org.jhipster.health.domain?dialect=org.hibernate.dialect.H2Dialect&hibernate.physical_naming_strategy=org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy&hibernate.implicit_naming_strategy=org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy" - defaultSchemaName "" - logLevel "debug" - classpath "$buildDir/classes/java/main" - } - } - - runList = project.ext.runList + destinationDirectory = file("$buildDir/reports/tests") + testResults.from(integrationTest) } gitProperties { @@ -153,146 +107,74 @@ gitProperties { keys = ["git.branch", "git.commit.id.abbrev", "git.commit.id.describe"] } -checkstyle { - toolVersion "${checkstyleVersion}" - configFile file("checkstyle.xml") - checkstyleTest.enabled = false -} -nohttp { - source.include "build.gradle", "README.md" +tasks.withType(com.gorylenko.GenerateGitPropertiesTask).configureEach { + outputs.doNotCacheIf("Task is always executed") { true } } configurations { providedRuntime implementation.exclude module: "spring-boot-starter-tomcat" - all { - resolutionStrategy { - // Inherited version from Spring Boot can't be used because of regressions: - // To be removed as soon as spring-boot use the same version - force 'org.liquibase:liquibase-core:4.15.0' - } - } -} - -repositories { - // Local maven repository is required for libraries built locally with maven like development jhipster-bom. - // mavenLocal() - mavenCentral() - //jhipster-needle-gradle-repositories - JHipster will add additional repositories } dependencies { - // import JHipster dependencies BOM - if (!project.hasProperty("gae")) { - implementation platform("tech.jhipster:jhipster-dependencies:${jhipsterDependenciesVersion}") - } - - // Use ", version: jhipsterDependenciesVersion, changing: true" if you want - // to use a SNAPSHOT release instead of a stable release - implementation group: "tech.jhipster", name: "jhipster-framework" - implementation "javax.annotation:javax.annotation-api" - implementation "org.springframework.boot:spring-boot-starter-cache" - implementation "com.fasterxml.jackson.module:jackson-module-jaxb-annotations" - implementation "com.fasterxml.jackson.datatype:jackson-datatype-hibernate5" implementation "com.fasterxml.jackson.datatype:jackson-datatype-hppc" implementation "com.fasterxml.jackson.datatype:jackson-datatype-jsr310" - testImplementation "org.testcontainers:junit-jupiter" - testImplementation "org.testcontainers:testcontainers" - implementation "org.springdoc:springdoc-openapi-webmvc-core" - implementation "com.zaxxer:HikariCP" + testImplementation("com.tngtech.archunit:archunit-junit5-api:${archunitJunit5Version}") { + exclude group: "org.slf4j", module: "slf4j-api" + } + testRuntimeOnly("com.tngtech.archunit:archunit-junit5-engine:${archunitJunit5Version}") { + exclude group: "org.slf4j", module: "slf4j-api" + } + implementation "io.dropwizard.metrics:metrics-core" + implementation "io.micrometer:micrometer-registry-prometheus" + implementation "jakarta.annotation:jakarta.annotation-api" implementation "org.apache.commons:commons-lang3" - implementation "javax.cache:cache-api" - implementation "org.ehcache:ehcache" - implementation "org.hibernate:hibernate-jcache" - annotationProcessor "org.hibernate:hibernate-jpamodelgen:${hibernateVersion}" - implementation "org.hibernate:hibernate-core" - implementation "org.hibernate.validator:hibernate-validator" - implementation "org.liquibase:liquibase-core" - liquibaseRuntime "org.liquibase:liquibase-core" - implementation "org.mapstruct:mapstruct:${mapstructVersion}" annotationProcessor "org.mapstruct:mapstruct-processor:${mapstructVersion}" - annotationProcessor "org.springframework.boot:spring-boot-configuration-processor:${springBootVersion}" + implementation "org.mapstruct:mapstruct:${mapstructVersion}" + annotationProcessor "org.springframework.boot:spring-boot-configuration-processor" implementation "org.springframework.boot:spring-boot-loader-tools" implementation "org.springframework.boot:spring-boot-starter-actuator" - implementation "org.springframework.boot:spring-boot-starter-data-jpa" - testImplementation "org.testcontainers:jdbc" - testImplementation "org.apache.commons:commons-collections4" - testImplementation "org.awaitility:awaitility:${awaitilityVersion}" - testImplementation "org.testcontainers:elasticsearch" - implementation "org.springframework.boot:spring-boot-starter-data-elasticsearch" + implementation "org.springframework.boot:spring-boot-starter-aop" implementation "org.springframework.boot:spring-boot-starter-logging" implementation "org.springframework.boot:spring-boot-starter-mail" + implementation "org.springframework.boot:spring-boot-starter-oauth2-resource-server" implementation "org.springframework.boot:spring-boot-starter-security" + testImplementation "org.springframework.boot:spring-boot-starter-test" implementation "org.springframework.boot:spring-boot-starter-thymeleaf" + implementation "org.springframework.boot:spring-boot-starter-undertow" + modules { + module("org.springframework.boot:spring-boot-starter-tomcat") { + replacedBy("org.springframework.boot:spring-boot-starter-undertow", "Use Undertow instead of Tomcat") + } + } + implementation "org.springframework.boot:spring-boot-starter-validation" implementation "org.springframework.boot:spring-boot-starter-web" - testImplementation "org.springframework.boot:spring-boot-starter-test" testImplementation "org.springframework.boot:spring-boot-test" testImplementation "org.springframework.security:spring-security-test" - testImplementation "com.tngtech.archunit:archunit-junit5-api:${archunitJunit5Version}" - testRuntimeOnly "com.tngtech.archunit:archunit-junit5-engine:${archunitJunit5Version}" - implementation "org.zalando:problem-spring-web" - implementation "org.springframework.boot:spring-boot-starter-undertow" - implementation "io.jsonwebtoken:jjwt-api" - if (!project.hasProperty("gae")) { - runtimeOnly "io.jsonwebtoken:jjwt-impl" - runtimeOnly "io.jsonwebtoken:jjwt-jackson" - } else { - implementation "io.jsonwebtoken:jjwt-impl" - implementation "io.jsonwebtoken:jjwt-jackson" - } + implementation libs.jhipster.framework + implementation libs.springdoc.openapi.starter.webmvc.api + implementation "org.springframework.boot:spring-boot-starter-data-jpa" implementation "org.springframework.security:spring-security-data" - implementation "io.micrometer:micrometer-registry-prometheus" - implementation "io.dropwizard.metrics:metrics-core" - liquibaseRuntime sourceSets.main.compileClasspath - liquibaseRuntime "org.liquibase.ext:liquibase-hibernate5:${liquibaseHibernate5Version}" - //jhipster-needle-gradle-dependency - JHipster will add additional dependencies here -} - -if (project.hasProperty("gae")) { - task createPom { - def basePath = 'build/resources/main/META-INF/maven' - doLast { - pom { - withXml(dependencyManagement.pomConfigurer) - }.writeTo("${basePath}/${project.group}/${project.name}/pom.xml") - } - } - bootJar.dependsOn = [createPom] + implementation "com.fasterxml.jackson.datatype:jackson-datatype-hibernate6" + implementation "org.hibernate.orm:hibernate-core" + implementation "org.hibernate.validator:hibernate-validator" + annotationProcessor "org.hibernate.orm:hibernate-jpamodelgen" + implementation "com.fasterxml.jackson.module:jackson-module-jaxb-annotations" + implementation "com.zaxxer:HikariCP" + annotationProcessor "org.glassfish.jaxb:jaxb-runtime" + testImplementation "org.testcontainers:jdbc" + testImplementation "org.testcontainers:junit-jupiter" + testImplementation "org.testcontainers:testcontainers" + implementation "org.springframework.boot:spring-boot-starter-data-elasticsearch" + testImplementation "org.awaitility:awaitility" + testImplementation "org.testcontainers:elasticsearch" + // jhipster-needle-gradle-dependency - JHipster will add additional dependencies here } task cleanResources(type: Delete) { delete "build/resources" } -wrapper { - gradleVersion = "7.4.2" -} - -task webapp_test(type: NpmTask, dependsOn: "npm_install") { - args = ["run", "webapp:test"] -} - -if (project.hasProperty("nodeInstall")) { - node { - version = "16.17.0" - npmVersion = "8.19.1" - download = true - } - - // Copy local node and npm to a fixed location for npmw - def fixedNode = tasks.register("fixedNode", Copy) { - from nodeSetup - into 'build/node' - } - tasks.named("nodeSetup").configure { finalizedBy fixedNode } - - def fixedNpm = tasks.register("fixedNpm", Copy) { - from npmSetup - into 'build/node' - } - tasks.named("npmSetup").configure { finalizedBy fixedNpm } -} -test.dependsOn webapp_test compileJava.dependsOn processResources processResources.dependsOn bootBuildInfo diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle new file mode 100644 index 00000000..d2af9831 --- /dev/null +++ b/buildSrc/build.gradle @@ -0,0 +1,18 @@ +plugins { + id 'groovy-gradle-plugin' +} + +repositories { + gradlePluginPortal() +} + +dependencies { + implementation libs.jib.plugin + implementation libs.modernizer.plugin + implementation libs.nohttp.plugin + implementation libs.sonarqube.plugin + implementation libs.spotless.plugin + implementation libs.node.gradle + // jhipster-needle-gradle-dependency - JHipster will add additional dependencies for convention plugins here + // jhipster-needle-gradle-build-src-dependency - Deprecated: JHipster will add additional dependencies for convention plugins here +} diff --git a/buildSrc/gradle/libs.versions.toml b/buildSrc/gradle/libs.versions.toml new file mode 100644 index 00000000..e5fea8e0 --- /dev/null +++ b/buildSrc/gradle/libs.versions.toml @@ -0,0 +1,15 @@ +[versions] +# jhipster-needle-gradle-dependency-catalog-version - JHipster will add additional versions for convention plugins heref +# jhipster-needle-gradle-build-src-dependency-catalog-version - Deprecated: JHipster will add additional versions for convention plugins here + +[libraries] +jib-plugin = { module = "com.google.cloud.tools:jib-gradle-plugin", version = "3.4.2" } +modernizer-plugin = { module = "com.github.andygoossens:gradle-modernizer-plugin", version = "1.9.2" } +nohttp-plugin = { module = "io.spring.nohttp:nohttp-gradle", version = "0.0.11" } +sonarqube-plugin = { module = "org.sonarsource.scanner.gradle:sonarqube-gradle-plugin", version = "5.0.0.4638" } +spotless-plugin = { module = "com.diffplug.spotless:spotless-plugin-gradle", version = "6.25.0" } +node-gradle = { module = "com.github.node-gradle:gradle-node-plugin", version = "7.0.2" } +# jhipster-needle-gradle-dependency-catalog-libraries - JHipster will add additional libraries versions + +[plugins] +# jhipster-needle-gradle-dependency-catalog-plugins - JHipster will add additional plugins versions diff --git a/buildSrc/src/main/groovy/jhipster.code-quality-conventions.gradle b/buildSrc/src/main/groovy/jhipster.code-quality-conventions.gradle new file mode 100644 index 00000000..921d4e6e --- /dev/null +++ b/buildSrc/src/main/groovy/jhipster.code-quality-conventions.gradle @@ -0,0 +1,63 @@ +plugins { + id "jacoco" + id "org.sonarqube" + id "com.diffplug.spotless" + id "com.github.andygoossens.gradle-modernizer-plugin" + id "io.spring.nohttp" +} + +jacoco { + toolVersion = "${libs.versions.jacoco.get()}" +} + +jacocoTestReport { + executionData tasks.withType(Test) + classDirectories.from = files(sourceSets.main.output.classesDirs) + sourceDirectories.from = files(sourceSets.main.java.srcDirs) + + reports { + xml.required = true + } +} + +file("sonar-project.properties").withReader { + Properties sonarProperties = new Properties() + sonarProperties.load(it) + + sonarProperties.each { key, value -> + sonarqube { + properties { + property key, value + } + } + } +} + +spotless { + java { + target 'src/*/java/**/*.java' + // removeUnusedImports() + } +} + +modernizer { + failOnViolations = true + includeTestClasses = true +} + +checkstyle { + toolVersion "${libs.versions.checkstyle.get()}" + configFile file("checkstyle.xml") + checkstyleTest.enabled = false +} + +nohttp { + source.include "build.gradle", "README.md" +} + +// workaround for https://github.com/checkstyle/checkstyle/issues/14123 +configurations.checkstyle { + resolutionStrategy.capabilitiesResolution.withCapability("com.google.collections:google-collections") { + select("com.google.guava:guava:0") + } +} \ No newline at end of file diff --git a/gradle/docker.gradle b/buildSrc/src/main/groovy/jhipster.docker-conventions.gradle similarity index 87% rename from gradle/docker.gradle rename to buildSrc/src/main/groovy/jhipster.docker-conventions.gradle index 697d409b..5a706b8f 100644 --- a/gradle/docker.gradle +++ b/buildSrc/src/main/groovy/jhipster.docker-conventions.gradle @@ -1,6 +1,10 @@ +plugins { + id "com.google.cloud.tools.jib" +} + jib { from { - image = "eclipse-temurin:11-jre-focal" + image = "eclipse-temurin:17-jre-focal" platforms { platform { architecture = "${findProperty('jibArchitecture') ?: 'amd64'}" @@ -25,5 +29,4 @@ jib { paths = file("src/main/docker/jib") permissions = ["/entrypoint.sh": "755"] } -} - +} \ No newline at end of file diff --git a/buildSrc/src/main/groovy/jhipster.node-gradle-conventions.gradle b/buildSrc/src/main/groovy/jhipster.node-gradle-conventions.gradle new file mode 100644 index 00000000..e4062761 --- /dev/null +++ b/buildSrc/src/main/groovy/jhipster.node-gradle-conventions.gradle @@ -0,0 +1,63 @@ +plugins { + id "com.github.node-gradle.node" +} + +if (project.hasProperty("nodeInstall")) { + node { + version = "20.12.2" + npmVersion = "10.6.0" + download = true + } + + // Copy local node and npm to a fixed location for npmw + def deleteOldNpm = tasks.register("deleteOldNpm", Delete) { + delete 'build/node/lib/node_modules/npm' + } + def fixedNode = tasks.register("fixedNode", Copy) { + from nodeSetup + into 'build/node' + finalizedBy deleteOldNpm + } + tasks.named("nodeSetup").configure { finalizedBy fixedNode } + + def fixedNpm = tasks.register("fixedNpm", Copy) { + from npmSetup + into 'build/node' + } + tasks.named("npmSetup").configure { finalizedBy fixedNpm } +} + +task webapp_test(type: NpmTask) { + inputs.property('appVersion', project.version) + inputs.files("package-lock.json") + .withPropertyName('package-lock') + .withPathSensitivity(PathSensitivity.RELATIVE) + inputs.files("build.gradle") + .withPropertyName('build.gradle') + .withPathSensitivity(PathSensitivity.RELATIVE) + inputs.files("angular.json") + .withPropertyName('angular.json') + .withPathSensitivity(PathSensitivity.RELATIVE) + inputs.files("tsconfig.json", "tsconfig.app.json") + .withPropertyName("tsconfig") + .withPathSensitivity(PathSensitivity.RELATIVE) + inputs.dir("webpack/") + .withPropertyName("webpack/") + .withPathSensitivity(PathSensitivity.RELATIVE) + inputs.dir("src/main/webapp/") + .withPropertyName("webapp-source-dir") + .withPathSensitivity(PathSensitivity.RELATIVE) + + outputs.dir("build/test-results/jest/") + .withPropertyName("jest-result-dir") + outputs.file("build/test-results/TESTS-results-jest.xml") + .withPropertyName("jest-result") + outputs.file("build/test-results/clover.xml") + .withPropertyName("clover-result") + + dependsOn npmInstall,compileTestJava + args = ["run", "webapp:test"] +} + +test.dependsOn webapp_test + diff --git a/buildSrc/src/main/groovy/jhipster.spring-cache-conventions.gradle b/buildSrc/src/main/groovy/jhipster.spring-cache-conventions.gradle new file mode 100644 index 00000000..75e4494d --- /dev/null +++ b/buildSrc/src/main/groovy/jhipster.spring-cache-conventions.gradle @@ -0,0 +1,7 @@ +dependencies { + implementation "org.springframework.boot:spring-boot-starter-cache" + implementation "javax.cache:cache-api" + implementation group: "org.ehcache", name: "ehcache", classifier: "jakarta" + implementation "org.hibernate.orm:hibernate-jcache" + // jhipster-needle-gradle-dependency - JHipster will add additional dependencies here +} diff --git a/cypress.config.ts b/cypress.config.ts index 596afa48..43e6d9fe 100644 --- a/cypress.config.ts +++ b/cypress.config.ts @@ -1,6 +1,6 @@ import { defineConfig } from 'cypress'; -export const defaultConfig = { +export default defineConfig({ video: false, fixturesFolder: 'src/test/javascript/cypress/fixtures', screenshotsFolder: 'build/cypress/screenshots', @@ -18,13 +18,12 @@ export const defaultConfig = { // We've imported your old cypress plugins here. // You may want to clean this up later by importing these. async setupNodeEvents(on, config) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-return return (await import('./src/test/javascript/cypress/plugins/index')).default(on, config); }, baseUrl: 'http://localhost:8080/', specPattern: 'src/test/javascript/cypress/e2e/**/*.cy.ts', supportFile: 'src/test/javascript/cypress/support/index.ts', - experimentalSessionAndOrigin: true, + experimentalRunAllSpecs: true, }, -}; - -export default defineConfig(defaultConfig); +}); diff --git a/gradle.properties b/gradle.properties index c3d34da0..4047d3fe 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,33 +2,16 @@ rootProject.name=twenty-one-points profile=dev # Dependency versions -jhipsterDependenciesVersion=7.9.3 -# The spring-boot version should match the one managed by -# https://mvnrepository.com/artifact/tech.jhipster/jhipster-dependencies/7.9.3 -springBootVersion=2.7.3 -# The hibernate version should match the one managed by -# https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-dependencies/2.7.3 --> -hibernateVersion=5.6.10.Final -mapstructVersion=1.5.2.Final -archunitJunit5Version=0.22.0 -liquibaseHibernate5Version=4.15.0 -liquibaseTaskPrefix=liquibase - - - -jaxbRuntimeVersion=4.0.0 +mapstructVersion=1.5.5.Final +archunitJunit5Version=1.3.0 # gradle plugin version -jibPluginVersion=3.2.1 -gitPropertiesPluginVersion=2.4.1 -gradleNodePluginVersion=3.4.0 -liquibasePluginVersion=2.1.1 -sonarqubePluginVersion=3.4.0.2513 -noHttpCheckstyleVersion=0.0.10 -checkstyleVersion=10.3.2 -modernizerPluginVersion=1.6.2 -awaitilityVersion=4.2.0 +gitPropertiesPluginVersion=2.4.2 +## Install and use a local version of node and npm. +nodeInstall +liquibaseTaskPrefix=liquibase +liquibasePluginVersion=2.2.2 # jhipster-needle-gradle-property - JHipster will add additional properties here ## below are some of the gradle performance improvement settings that can be used as required, these are not enabled by default @@ -60,6 +43,3 @@ awaitilityVersion=4.2.0 ## uncomment the below line to enable the selective mode #org.gradle.configureondemand=true - -## Install and use a local version of node and npm. -nodeInstall diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 00000000..dd489182 --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,13 @@ +[versions] +jacoco = "0.8.12" +checkstyle = "10.16.0" +# jhipster-needle-gradle-dependency-catalog-version - JHipster will add additional versions for convention plugins heref + +[libraries] +jhipster-framework = { module = "tech.jhipster:jhipster-framework", version = "8.4.0" } +springdoc-openapi-starter-webmvc-api = { module = "org.springdoc:springdoc-openapi-starter-webmvc-api", version = "2.5.0" } +# jhipster-needle-gradle-dependency-catalog-libraries - JHipster will add additional libraries versions + +[plugins] +spring-boot = { id = "org.springframework.boot", version = "3.2.5" } +# jhipster-needle-gradle-dependency-catalog-plugins - JHipster will add additional plugins versions diff --git a/gradle/liquibase.gradle b/gradle/liquibase.gradle new file mode 100644 index 00000000..913ae523 --- /dev/null +++ b/gradle/liquibase.gradle @@ -0,0 +1,53 @@ +configurations { + liquibaseRuntime.extendsFrom sourceSets.main.compileClasspath +} + +dependencies { + implementation "org.liquibase:liquibase-core" + liquibaseRuntime "org.liquibase:liquibase-core" + // Dependency required to parse options. Refer to https://github.com/liquibase/liquibase-gradle-plugin/tree/Release_2.2.0#news. + liquibaseRuntime "info.picocli:picocli:4.7.5" + if (project.hasProperty("prod") || project.hasProperty("gae")) { + liquibaseRuntime "org.postgresql:postgresql" + } + liquibaseRuntime "org.liquibase.ext:liquibase-hibernate6:${dependencyManagement.importedProperties['liquibase.version']}" + // jhipster-needle-gradle-dependency - JHipster will add additional dependencies here +} + +project.ext.diffChangelogFile = "src/main/resources/config/liquibase/changelog/" + new Date().format("yyyyMMddHHmmss") + "_changelog.xml" +if (!project.hasProperty("runList")) { + project.ext.runList = "main" +} + +liquibase { + activities { + main { + driver "org.h2.Driver" + url "jdbc:h2:file:./build/h2db/db/TwentyOnePoints" + username "TwentyOnePoints" + changelogFile "src/main/resources/config/liquibase/master.xml" + logLevel "debug" + classpath "src/main/resources/" + } + diffLog { + driver "org.h2.Driver" + url "jdbc:h2:file:./build/h2db/db/TwentyOnePoints" + username "TwentyOnePoints" + changelogFile project.ext.diffChangelogFile + referenceUrl "hibernate:spring:org.jhipster.health.domain?dialect=org.hibernate.dialect.H2Dialect&hibernate.physical_naming_strategy=org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy&hibernate.implicit_naming_strategy=org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy" + logLevel "debug" + classpath "$buildDir/classes/java/main" + } + } + + runList = project.ext.runList +} + +liquibaseDiff.dependsOn compileJava +liquibaseDiffChangelog.dependsOn compileJava + +ext { + if (project.hasProperty("no-liquibase")) { + springProfiles += ",no-liquibase" + } +} diff --git a/gradle/profile_dev.gradle b/gradle/profile_dev.gradle index 62c8cd18..ee0a90d6 100644 --- a/gradle/profile_dev.gradle +++ b/gradle/profile_dev.gradle @@ -1,34 +1,36 @@ sourceSets { test { java { - exclude 'org/jhipster/health/config/PostgreSqlTestContainer.java' + exclude 'org/jhipster/health//config/PostgreSqlTestContainer.java' } } } + +configurations { + all { + resolutionStrategy { + } + } +} + dependencies { - developmentOnly "org.springframework.boot:spring-boot-devtools:${springBootVersion}" + developmentOnly "org.springframework.boot:spring-boot-devtools" implementation "com.h2database:h2" testImplementation "org.testcontainers:postgresql" } -def profiles = "dev" -if (project.hasProperty("no-liquibase")) { - profiles += ",no-liquibase" -} -if (project.hasProperty("tls")) { - profiles += ",tls" +ext { + springProfiles = "dev" + springProfiles } springBoot { buildInfo { - properties { - time = null - } + excludes = ['time'] } } bootRun { - args = ["--spring.profiles.active=${profiles}"] + args = ["--spring.profiles.active=${springProfiles}"] } task webapp(type: NpmTask) { @@ -62,13 +64,13 @@ task webapp(type: NpmTask) { processResources { inputs.property('version', version) - inputs.property('springProfiles', profiles) + inputs.property('springProfiles', springProfiles) filesMatching("**/application.yml") { filter { it.replace("#project.version#", version) } filter { - it.replace("#spring.profiles.active#", profiles) + it.replace("@spring.profiles.active@", springProfiles) } } } @@ -87,7 +89,7 @@ task integrationTest(type: Test) { // uncomment if the tests reports are not generated // see https://github.com/jhipster/generator-jhipster/pull/2771 and https://github.com/jhipster/generator-jhipster/pull/4484 // ignoreFailures true - reports.html.enabled = false + reports.html.required = false } integrationTest.dependsOn test diff --git a/gradle/profile_prod.gradle b/gradle/profile_prod.gradle index 72796654..aa5c34f5 100644 --- a/gradle/profile_prod.gradle +++ b/gradle/profile_prod.gradle @@ -1,20 +1,22 @@ + +configurations { + all { + resolutionStrategy { + } + } +} + dependencies { implementation "org.postgresql:postgresql" - liquibaseRuntime "org.postgresql:postgresql" testImplementation "org.testcontainers:postgresql" } -def profiles = "prod" -if (project.hasProperty("no-liquibase")) { - profiles += ",no-liquibase" -} - -if (project.hasProperty("api-docs")) { - profiles += ",api-docs" -} +ext { + springProfiles = "prod" + springProfiles -if (project.hasProperty("e2e")) { - profiles += ",e2e" + if (project.hasProperty("api-docs")) { + springProfiles += ",api-docs" + } } springBoot { @@ -22,23 +24,24 @@ springBoot { } bootRun { - args = ["--spring.profiles.active=${profiles}"] + args = ["--spring.profiles.active=${springProfiles}"] } -task webapp(type: NpmTask, dependsOn: "npm_install") { +task webapp(type: NpmTask) { + dependsOn npmInstall args = ["run", "webapp:prod"] environment = [APP_VERSION: project.version] } processResources { inputs.property('version', version) - inputs.property('springProfiles', profiles) + inputs.property('springProfiles', springProfiles) filesMatching("**/application.yml") { filter { it.replace("#project.version#", version) } filter { - it.replace("#spring.profiles.active#", profiles) + it.replace("@spring.profiles.active@", springProfiles) } } } @@ -57,7 +60,7 @@ task integrationTest(type: Test) { // uncomment if the tests reports are not generated // see https://github.com/jhipster/generator-jhipster/pull/2771 and https://github.com/jhipster/generator-jhipster/pull/4484 // ignoreFailures true - reports.html.enabled = false + reports.html.required = false } integrationTest.dependsOn test diff --git a/gradle/sonar.gradle b/gradle/sonar.gradle deleted file mode 100644 index bdd9c8a9..00000000 --- a/gradle/sonar.gradle +++ /dev/null @@ -1,26 +0,0 @@ -jacoco { - toolVersion = "0.8.8" -} - -jacocoTestReport { - executionData tasks.withType(Test) - classDirectories.from = files(sourceSets.main.output.classesDirs) - sourceDirectories.from = files(sourceSets.main.java.srcDirs) - - reports { - xml.enabled = true - } -} - -file("sonar-project.properties").withReader { - Properties sonarProperties = new Properties() - sonarProperties.load(it) - - sonarProperties.each { key, value -> - sonarqube { - properties { - property key, value - } - } - } -} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 41d9927a4d4fb3f96a785543079b8df6723c946b..c1962a79e29d3e0ab67b14947c167a862655af9b 100644 GIT binary patch delta 40199 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 delta 37942 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 \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG=`dirname "$PRG"`"/$link" - fi +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac done -SAVED="`pwd`" -cd "`dirname \"$PRG\"`/" >/dev/null -APP_HOME="`pwd -P`" -cd "$SAVED" >/dev/null -APP_NAME="Gradle" -APP_BASE_NAME=`basename "$0"` - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD="maximum" +MAX_FD=maximum warn () { echo "$*" -} +} >&2 die () { echo echo "$*" echo exit 1 -} +} >&2 # OS specific support (must be 'true' or 'false'). cygwin=false msys=false darwin=false nonstop=false -case "`uname`" in - CYGWIN* ) - cygwin=true - ;; - Darwin* ) - darwin=true - ;; - MINGW* ) - msys=true - ;; - NONSTOP* ) - nonstop=true - ;; +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar @@ -87,9 +118,9 @@ CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACMD=$JAVA_HOME/jre/sh/java else - JAVACMD="$JAVA_HOME/bin/java" + JAVACMD=$JAVA_HOME/bin/java fi if [ ! -x "$JAVACMD" ] ; then die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME @@ -98,7 +129,7 @@ Please set the JAVA_HOME variable in your environment to match the location of your Java installation." fi else - JAVACMD="java" + JAVACMD=java which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the @@ -106,80 +137,109 @@ location of your Java installation." fi # Increase the maximum file descriptors if we can. -if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then - MAX_FD_LIMIT=`ulimit -H -n` - if [ $? -eq 0 ] ; then - if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then - MAX_FD="$MAX_FD_LIMIT" - fi - ulimit -n $MAX_FD - if [ $? -ne 0 ] ; then - warn "Could not set maximum file descriptor limit: $MAX_FD" - fi - else - warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" - fi +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac fi -# For Darwin, add options to specify how the application appears in the dock -if $darwin; then - GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" -fi +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. # For Cygwin or MSYS, switch paths to Windows format before running java -if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then - APP_HOME=`cygpath --path --mixed "$APP_HOME"` - CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` - - JAVACMD=`cygpath --unix "$JAVACMD"` - - # We build the pattern for arguments to be converted via cygpath - ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` - SEP="" - for dir in $ROOTDIRSRAW ; do - ROOTDIRS="$ROOTDIRS$SEP$dir" - SEP="|" - done - OURCYGPATTERN="(^($ROOTDIRS))" - # Add a user-defined pattern to the cygpath arguments - if [ "$GRADLE_CYGPATTERN" != "" ] ; then - OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" - fi +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + # Now convert the arguments - kludge to limit ourselves to /bin/sh - i=0 - for arg in "$@" ; do - CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` - CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option - - if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition - eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` - else - eval `echo args$i`="\"$arg\"" + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) fi - i=`expr $i + 1` + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg done - case $i in - 0) set -- ;; - 1) set -- "$args0" ;; - 2) set -- "$args0" "$args1" ;; - 3) set -- "$args0" "$args1" "$args2" ;; - 4) set -- "$args0" "$args1" "$args2" "$args3" ;; - 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; - 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; - 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; - 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; - 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; - esac fi -# Escape application args -save () { - for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done - echo " " -} -APP_ARGS=`save "$@"` -# Collect all arguments for the java command, following the shell quoting and substitution rules -eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat index 107acd32..93e3f59f 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -14,7 +14,7 @@ @rem limitations under the License. @rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @@ -25,7 +25,8 @@ if "%OS%"=="Windows_NT" setlocal set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute echo. echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. @@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar :end @rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd +if %ERRORLEVEL% equ 0 goto mainEnd :fail rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% :mainEnd if "%OS%"=="Windows_NT" endlocal diff --git a/npmw b/npmw index 12a91e52..785f188f 100755 --- a/npmw +++ b/npmw @@ -5,7 +5,7 @@ basedir=`dirname "$0"` if [ -f "$basedir/mvnw" ]; then bindir="$basedir/target/node" repodir="$basedir/target/node/node_modules" - installCommand="$basedir/mvnw -Pwebapp frontend:install-node-and-npm@install-node-and-npm" + installCommand="$basedir/mvnw --batch-mode -ntp -Pwebapp frontend:install-node-and-npm@install-node-and-npm" PATH="$basedir/$builddir/:$PATH" NPM_EXE="$basedir/$builddir/node_modules/npm/bin/npm-cli.js" diff --git a/npmw.cmd b/npmw.cmd index e45f868d..b6e79809 100644 --- a/npmw.cmd +++ b/npmw.cmd @@ -17,7 +17,7 @@ if exist "%NPMW_DIR%mvnw.cmd" ( ) if not exist %NPM_EXE% ( - call %INSTALL_NPM_COMMAND% + call %INSTALL_NPM_COMMAND% ) if exist %NODE_EXE% ( diff --git a/package.json b/package.json index 4ff4c27c..9d56ef9f 100644 --- a/package.json +++ b/package.json @@ -2,48 +2,45 @@ "name": "twenty-one-points", "version": "0.0.1-SNAPSHOT", "private": true, - "description": "Description for TwentyOnePoints", + "description": "Description for Twenty One Points", "license": "UNLICENSED", "scripts": { "app:start": "./gradlew", - "backend:build-cache": "npm run backend:info && npm run backend:nohttp:test && npm run ci:e2e:package", + "app:up": "docker compose -f src/main/docker/app.yml up --wait", + "backend:build-cache": "npm run backend:info && npm run backend:nohttp:test && npm run ci:e2e:package -- -x webapp -x webapp_test", "backend:doc:test": "./gradlew javadoc -x webapp -x webapp_test", "backend:info": "./gradlew -v", "backend:nohttp:test": "./gradlew checkstyleNohttp -x webapp -x webapp_test", "backend:start": "./gradlew -x webapp -x webapp_test", - "backend:unit:test": "./gradlew test integrationTest -x webapp -x webapp_test -Dlogging.level.ROOT=OFF -Dlogging.level.org.zalando=OFF -Dlogging.level.tech.jhipster=OFF -Dlogging.level.org.jhipster.health=OFF -Dlogging.level.org.springframework=OFF -Dlogging.level.org.springframework.web=OFF -Dlogging.level.org.springframework.security=OFF", + "backend:unit:test": "./gradlew test integrationTest -x webapp -x webapp_test -Dlogging.level.ROOT=OFF -Dlogging.level.tech.jhipster=OFF -Dlogging.level.org.jhipster.health=OFF -Dlogging.level.org.springframework=OFF -Dlogging.level.org.springframework.web=OFF -Dlogging.level.org.springframework.security=OFF", "build": "npm run webapp:prod --", "build-watch": "concurrently 'npm run webapp:build:dev -- --watch' npm:backend:start", "ci:backend:test": "npm run backend:info && npm run backend:doc:test && npm run backend:nohttp:test && npm run backend:unit:test -- -P$npm_package_config_default_environment", "ci:e2e:package": "npm run java:$npm_package_config_packaging:$npm_package_config_default_environment -- -Pe2e -Denforcer.skip=true", "postci:e2e:package": "cp build/libs/*.$npm_package_config_packaging e2e.$npm_package_config_packaging", "ci:e2e:prepare": "npm run ci:e2e:prepare:docker", - "ci:e2e:prepare:docker": "npm run docker:db:up && npm run docker:others:up && docker ps -a", + "ci:e2e:prepare:docker": "npm run services:up --if-present && docker ps -a", "ci:e2e:run": "concurrently -k -s first \"npm run ci:e2e:server:start\" \"npm run e2e:headless\"", - "preci:e2e:server:start": "npm run docker:db:await --if-present && npm run docker:others:await --if-present", - "ci:e2e:server:start": "java -jar e2e.$npm_package_config_packaging --spring.profiles.active=e2e,$npm_package_config_default_environment -Dlogging.level.ROOT=OFF -Dlogging.level.org.zalando=OFF -Dlogging.level.tech.jhipster=OFF -Dlogging.level.org.jhipster.health=OFF -Dlogging.level.org.springframework=OFF -Dlogging.level.org.springframework.web=OFF -Dlogging.level.org.springframework.security=OFF --logging.level.org.springframework.web=ERROR", - "ci:e2e:teardown": "npm run ci:e2e:teardown:docker", - "ci:e2e:teardown:docker": "npm run docker:db:down --if-present && npm run docker:others:down && docker ps -a", + "preci:e2e:server:start": "npm run services:db:await --if-present && npm run services:others:await --if-present", + "ci:e2e:server:start": "java -jar e2e.$npm_package_config_packaging --spring.profiles.active=e2e,$npm_package_config_default_environment -Dlogging.level.ROOT=OFF -Dlogging.level.tech.jhipster=OFF -Dlogging.level.org.jhipster.health=OFF -Dlogging.level.org.springframework=OFF -Dlogging.level.org.springframework.web=OFF -Dlogging.level.org.springframework.security=OFF --logging.level.org.springframework.web=ERROR", + "ci:e2e:teardown": "npm run ci:e2e:teardown:docker --if-present", + "ci:e2e:teardown:docker": "docker compose -f src/main/docker/services.yml down -v && docker ps -a", "ci:frontend:build": "npm run webapp:build:$npm_package_config_default_environment", "ci:frontend:test": "npm run ci:frontend:build && npm test", - "ci:server:await": "echo \"Waiting for server at port $npm_package_config_backend_port to start\" && wait-on -t 180000 http-get://localhost:$npm_package_config_backend_port/management/health && echo \"Server at port $npm_package_config_backend_port started\"", - "clean-www": "rimraf build/resources/main/static/app/{src,build/}", - "cleanup": "rimraf build/resources/main/static/", + "ci:server:await": "echo \"Waiting for server at port $npm_package_config_backend_port to start\" && wait-on -t 180000 http-get://127.0.0.1:$npm_package_config_backend_port/management/health && echo \"Server at port $npm_package_config_backend_port started\"", + "clean-www": "rimraf build/resources/main/static/", + "cleanup": "rimraf build/", "cypress": "cypress open --e2e", - "docker:app:up": "docker-compose -f src/main/docker/app.yml up -d", - "docker:db:down": "docker-compose -f src/main/docker/postgresql.yml down -v", - "docker:db:up": "docker-compose -f src/main/docker/postgresql.yml up -d", - "docker:elasticsearch:down": "docker-compose -f src/main/docker/elasticsearch.yml down -v", - "docker:elasticsearch:up": "docker-compose -f src/main/docker/elasticsearch.yml up -d", - "docker:others:await": "echo \"Waiting for Elasticsearch to start\" && wait-on -t 180000 \"http-get://localhost:9200/_cluster/health?wait_for_status=green&timeout=60s\" && echo \"Elasticsearch started\"", - "docker:others:down": "npm run docker:elasticsearch:down", - "predocker:others:up": "", - "docker:others:up": "npm run docker:elasticsearch:up", + "docker:db:down": "docker compose -f src/main/docker/postgresql.yml down -v", + "docker:db:up": "docker compose -f src/main/docker/postgresql.yml up --wait", + "docker:elasticsearch:down": "docker compose -f src/main/docker/elasticsearch.yml down -v", + "docker:elasticsearch:up": "docker compose -f src/main/docker/elasticsearch.yml up --wait", "e2e": "npm run e2e:cypress:headed --", - "e2e:cypress": "cypress run --e2e --browser chrome --record ${CYPRESS_ENABLE_RECORD:-false}", + "e2e:cypress": "cypress run --e2e --browser chrome", "e2e:cypress:headed": "npm run e2e:cypress -- --headed", + "e2e:cypress:record": "npm run e2e:cypress -- --record", "e2e:dev": "concurrently -k -s first \"./gradlew\" \"npm run e2e\"", - "e2e:devserver": "concurrently -k -s first \"npm run backend:start\" \"npm start\" \"wait-on -t 180000 http-get://localhost:9000 && npm run e2e:headless -- -c baseUrl=http://localhost:9000\"", + "e2e:devserver": "concurrently -k -s first \"npm run backend:start\" \"npm start\" \"wait-on -t 180000 http-get://127.0.0.1:9000 && npm run e2e:headless -- -c baseUrl=http://localhost:9000\"", "pree2e:headless": "npm run ci:server:await", "e2e:headless": "npm run e2e:cypress --", "java:docker": "./gradlew bootJar -Pprod jibDockerBuild", @@ -59,10 +56,11 @@ "jest": "jest --coverage --logHeapUsage --maxWorkers=2 --config jest.conf.js", "lint": "eslint . --ext .js,.ts", "lint:fix": "npm run lint -- --fix", - "prepare": "husky install", + "prepare": "husky", "prettier:check": "prettier --check \"{,src/**/,webpack/,.blueprint/**/}*.{md,json,yml,html,cjs,mjs,js,ts,tsx,css,scss,java}\"", "prettier:format": "prettier --write \"{,src/**/,webpack/,.blueprint/**/}*.{md,json,yml,html,cjs,mjs,js,ts,tsx,css,scss,java}\"", "serve": "npm run start --", + "services:up": "docker compose -f src/main/docker/services.yml up --wait", "start": "ng serve --hmr", "start-tls": "npm run webapp:dev-ssl", "pretest": "npm run lint", @@ -84,81 +82,82 @@ "packaging": "jar" }, "dependencies": { - "@angular/common": "14.2.0", - "@angular/compiler": "14.2.0", - "@angular/core": "14.2.0", - "@angular/forms": "14.2.0", - "@angular/localize": "14.2.0", - "@angular/platform-browser": "14.2.0", - "@angular/platform-browser-dynamic": "14.2.0", - "@angular/router": "14.2.0", - "@fortawesome/angular-fontawesome": "0.11.1", - "@fortawesome/fontawesome-svg-core": "6.2.0", - "@fortawesome/free-solid-svg-icons": "6.2.0", - "@ng-bootstrap/ng-bootstrap": "13.0.0", - "@ngx-translate/core": "14.0.0", - "@ngx-translate/http-loader": "7.0.0", - "@popperjs/core": "2.11.6", - "bootstrap": "5.2.0", - "dayjs": "1.11.5", - "ngx-infinite-scroll": "14.0.0", - "ngx-webstorage": "10.0.1", - "rxjs": "7.5.6", - "tslib": "2.4.0", - "zone.js": "0.11.6" + "@angular/common": "17.3.6", + "@angular/compiler": "17.3.6", + "@angular/core": "17.3.6", + "@angular/forms": "17.3.6", + "@angular/localize": "17.3.6", + "@angular/platform-browser": "17.3.6", + "@angular/platform-browser-dynamic": "17.3.6", + "@angular/router": "17.3.6", + "@fortawesome/angular-fontawesome": "0.14.1", + "@fortawesome/fontawesome-svg-core": "6.5.2", + "@fortawesome/free-solid-svg-icons": "6.5.2", + "@ng-bootstrap/ng-bootstrap": "16.0.0", + "@ngx-translate/core": "15.0.0", + "@ngx-translate/http-loader": "8.0.0", + "@popperjs/core": "2.11.8", + "bootstrap": "5.3.3", + "dayjs": "1.11.11", + "ngx-infinite-scroll": "17.0.0", + "rxjs": "7.8.1", + "tslib": "2.6.2", + "zone.js": "0.14.4" }, "devDependencies": { - "@angular-builders/custom-webpack": "14.0.1", - "@angular-builders/jest": "14.0.1", - "@angular-devkit/build-angular": "14.2.1", - "@angular-eslint/eslint-plugin": "14.0.3", - "@angular/cli": "14.2.1", - "@angular/compiler-cli": "14.2.0", - "@angular/service-worker": "14.2.0", - "@types/jest": "28.1.8", - "@types/node": "16.11.56", - "@typescript-eslint/eslint-plugin": "5.36.1", - "@typescript-eslint/parser": "5.36.1", - "browser-sync": "2.27.10", + "@angular-builders/custom-webpack": "17.0.2", + "@angular-builders/jest": "17.0.3", + "@angular-devkit/build-angular": "17.3.6", + "@angular-eslint/eslint-plugin": "17.3.0", + "@angular/cli": "17.3.6", + "@angular/compiler-cli": "17.3.6", + "@angular/service-worker": "17.3.6", + "@types/jest": "29.5.12", + "@types/node": "20.11.25", + "@typescript-eslint/eslint-plugin": "7.7.1", + "@typescript-eslint/parser": "7.7.1", + "browser-sync": "3.0.2", "browser-sync-webpack-plugin": "2.3.0", - "concurrently": "7.3.0", - "copy-webpack-plugin": "11.0.0", - "cypress": "10.7.0", - "eslint": "8.23.0", - "eslint-config-prettier": "8.5.0", - "eslint-plugin-cypress": "2.12.1", - "eslint-webpack-plugin": "3.2.0", - "folder-hash": "4.0.2", - "generator-jhipster": "7.9.3", - "generator-jhipster-es-entity-reindexer": "1.0.4", - "husky": "7.0.4", - "jest": "28.1.3", - "jest-date-mock": "1.0.8", - "jest-environment-jsdom": "28.1.3", - "jest-junit": "14.0.1", - "jest-preset-angular": "12.2.2", - "jest-sonar": "0.2.12", - "lint-staged": "13.0.3", + "buffer": "6.0.3", + "concurrently": "8.2.2", + "copy-webpack-plugin": "12.0.2", + "cypress": "13.8.1", + "eslint": "8.57.0", + "eslint-config-prettier": "9.1.0", + "eslint-plugin-cypress": "3.0.2", + "eslint-webpack-plugin": "4.1.0", + "folder-hash": "4.0.4", + "generator-jhipster": "8.4.0", + "generator-jhipster-migrate": "1.1.0", + "husky": "9.0.11", + "jest": "29.7.0", + "jest-date-mock": "1.0.10", + "jest-environment-jsdom": "29.7.0", + "jest-junit": "16.0.0", + "jest-preset-angular": "14.0.3", + "jest-sonar": "0.2.16", + "lint-staged": "15.2.2", "merge-jsons-webpack-plugin": "2.0.1", - "prettier": "2.7.1", - "prettier-plugin-java": "1.6.2", - "prettier-plugin-packagejson": "2.2.18", - "rimraf": "3.0.2", - "swagger-ui-dist": "4.14.0", - "ts-jest": "28.0.8", - "typescript": "4.8.2", - "wait-on": "6.0.1", - "webpack-bundle-analyzer": "4.6.1", - "webpack-merge": "5.8.0", + "prettier": "3.2.5", + "prettier-plugin-java": "2.6.0", + "prettier-plugin-packagejson": "2.5.0", + "rimraf": "5.0.5", + "swagger-ui-dist": "5.17.2", + "ts-jest": "29.1.2", + "typescript": "5.4.5", + "wait-on": "7.2.0", + "webpack-bundle-analyzer": "4.10.2", + "webpack-merge": "5.10.0", "webpack-notifier": "1.15.0" }, "engines": { - "node": ">=16.17.0" + "node": ">=20.12.2" }, "cacheDirectories": [ "node_modules" ], "overrides": { - "webpack": "5.74.0" + "browser-sync": "3.0.2", + "webpack": "5.91.0" } } diff --git a/settings.gradle b/settings.gradle index 63b3b73c..acfa302c 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,21 +1,12 @@ pluginManagement { repositories { - mavenCentral() - maven { url 'https://repo.spring.io/milestone' } - gradlePluginPortal() - //jhipster-needle-gradle-plugin-management-repositories - JHipster will add additional entries here + // jhipster-needle-gradle-plugin-management-repositories - JHipster will add additional entries here } plugins { - id 'org.springframework.boot' version "${springBootVersion}" - id 'com.google.cloud.tools.jib' version "${jibPluginVersion}" id 'com.gorylenko.gradle-git-properties' version "${gitPropertiesPluginVersion}" - id 'com.github.node-gradle.node' version "${gradleNodePluginVersion}" - id 'org.liquibase.gradle' version "${liquibasePluginVersion}" - id 'org.sonarqube' version "${sonarqubePluginVersion}" - id "io.spring.nohttp" version "${noHttpCheckstyleVersion}" - id 'com.github.andygoossens.gradle-modernizer-plugin' version "${modernizerPluginVersion}" - //jhipster-needle-gradle-plugin-management-plugins - JHipster will add additional entries here + id "org.liquibase.gradle" version "${liquibasePluginVersion}" + // jhipster-needle-gradle-plugin-management-plugins - JHipster will add additional entries here } } diff --git a/sonar-project.properties b/sonar-project.properties index 300efd53..c1e3f084 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -1,34 +1,43 @@ -sonar.projectKey=TwentyOnePoints -sonar.projectName=TwentyOnePoints generated by jhipster +sonar.projectKey = TwentyOnePoints +sonar.projectName = TwentyOnePoints generated by jhipster -# Typescript tests files must be inside sources and tests, othewise `INFO: Test execution data ignored for 80 unknown files, including:` is shown. -sonar.sources=src -sonar.tests=src -sonar.host.url=http://localhost:9001 +# Typescript tests files must be inside sources and tests, otherwise `INFO: Test execution data ignored for 80 unknown files, including:` +# is shown. +sonar.sources = src +sonar.tests = src +sonar.host.url = http://localhost:9001 -sonar.test.inclusions=src/test/**/*.*, src/main/webapp/app/**/*.spec.ts -sonar.coverage.jacoco.xmlReportPaths=build/reports/jacoco/test/jacocoTestReport.xml -sonar.java.codeCoveragePlugin=jacoco -sonar.junit.reportPaths=build/test-results/test, build/test-results/integrationTest -sonar.testExecutionReportPaths=build/test-results/jest/TESTS-results-sonar.xml -sonar.javascript.lcov.reportPaths=build/test-results/lcov.info +sonar.test.inclusions = src/test/**/*.*, src/main/webapp/app/**/*.spec.ts +sonar.coverage.jacoco.xmlReportPaths = build/reports/jacoco/test/jacocoTestReport.xml +sonar.java.codeCoveragePlugin = jacoco +sonar.junit.reportPaths = build/test-results/test, build/test-results/integrationTest +sonar.testExecutionReportPaths = build/test-results/jest/TESTS-results-sonar.xml +sonar.javascript.lcov.reportPaths = build/test-results/lcov.info -sonar.sourceEncoding=UTF-8 -sonar.exclusions=src/main/webapp/content/**/*.*, src/main/webapp/i18n/*.js, build/resources/main/static/**/*.* +sonar.sourceEncoding = UTF-8 +sonar.exclusions = src/main/webapp/content/**/*.*, src/main/webapp/i18n/*.js, build/resources/main/static/**/*.* + +sonar.issue.ignore.multicriteria = S1192,S125,S3437,S4502,S4684,S5145,UndocumentedApi -sonar.issue.ignore.multicriteria=S3437,S4502,S4684,S5145,UndocumentedApi # Rule https://rules.sonarsource.com/java/RSPEC-3437 is ignored, as a JPA-managed field cannot be transient -sonar.issue.ignore.multicriteria.S3437.resourceKey=src/main/java/**/* -sonar.issue.ignore.multicriteria.S3437.ruleKey=squid:S3437 +sonar.issue.ignore.multicriteria.S3437.resourceKey = src/main/java/**/* +sonar.issue.ignore.multicriteria.S3437.ruleKey = squid:S3437 # Rule https://rules.sonarsource.com/java/RSPEC-4502 is ignored, as for JWT tokens we are not subject to CSRF attack -sonar.issue.ignore.multicriteria.S4502.resourceKey=src/main/java/**/* -sonar.issue.ignore.multicriteria.S4502.ruleKey=java:S4502 +sonar.issue.ignore.multicriteria.S4502.resourceKey = src/main/java/**/* +sonar.issue.ignore.multicriteria.S4502.ruleKey = java:S4502 # Rule https://rules.sonarsource.com/java/RSPEC-4684 -sonar.issue.ignore.multicriteria.S4684.resourceKey=src/main/java/**/* -sonar.issue.ignore.multicriteria.S4684.ruleKey=java:S4684 +sonar.issue.ignore.multicriteria.S4684.resourceKey = src/main/java/**/* +sonar.issue.ignore.multicriteria.S4684.ruleKey = java:S4684 # Rule https://rules.sonarsource.com/java/RSPEC-5145 log filter is applied -sonar.issue.ignore.multicriteria.S5145.resourceKey=src/main/java/**/* -sonar.issue.ignore.multicriteria.S5145.ruleKey=javasecurity:S5145 -# Rule https://rules.sonarsource.com/java/RSPEC-1176 is ignored, as we want to follow "clean code" guidelines and classes, methods and arguments names should be self-explanatory -sonar.issue.ignore.multicriteria.UndocumentedApi.resourceKey=src/main/java/**/* -sonar.issue.ignore.multicriteria.UndocumentedApi.ruleKey=squid:UndocumentedApi +sonar.issue.ignore.multicriteria.S5145.resourceKey = src/main/java/**/* +sonar.issue.ignore.multicriteria.S5145.ruleKey = javasecurity:S5145 +# Rule https://rules.sonarsource.com/java/RSPEC-1176 is ignored, as we want to follow "clean code" guidelines and classes, methods and +# arguments names should be self-explanatory +sonar.issue.ignore.multicriteria.UndocumentedApi.resourceKey = src/main/java/**/* +sonar.issue.ignore.multicriteria.UndocumentedApi.ruleKey = squid:UndocumentedApi +# Rule https://rules.sonarsource.com/java/RSPEC-1192 +sonar.issue.ignore.multicriteria.S1192.resourceKey = src/main/java/**/CacheConfiguration.java +sonar.issue.ignore.multicriteria.S1192.ruleKey = java:S1192 +# Rule https://rules.sonarsource.com/xml/RSPEC-125 +sonar.issue.ignore.multicriteria.S125.resourceKey = src/main/resources/logback-spring.xml +sonar.issue.ignore.multicriteria.S125.ruleKey = xml:S125 diff --git a/src/main/docker/app.yml b/src/main/docker/app.yml index 8a0e8d29..1622a810 100644 --- a/src/main/docker/app.yml +++ b/src/main/docker/app.yml @@ -1,41 +1,36 @@ # This configuration is intended for development purpose, it's **your** responsibility to harden it for production -version: '3.8' +name: twentyonepoints services: - twentyonepoints-app: + app: image: twentyonepoints environment: - _JAVA_OPTIONS=-Xmx512m -Xms256m - SPRING_PROFILES_ACTIVE=prod,api-docs - - MANAGEMENT_METRICS_EXPORT_PROMETHEUS_ENABLED=true - - SPRING_DATASOURCE_URL=jdbc:postgresql://twentyonepoints-postgresql:5432/TwentyOnePoints - - SPRING_LIQUIBASE_URL=jdbc:postgresql://twentyonepoints-postgresql:5432/TwentyOnePoints - - JHIPSTER_SLEEP=30 # gives time for other services to boot before the application - - SPRING_ELASTICSEARCH_URIS=http://twentyonepoints-elasticsearch:9200 - # If you want to expose these ports outside your dev PC, - # remove the "127.0.0.1:" prefix + - MANAGEMENT_PROMETHEUS_METRICS_EXPORT_ENABLED=true + - SPRING_DATASOURCE_URL=jdbc:postgresql://postgresql:5432/TwentyOnePoints + - SPRING_LIQUIBASE_URL=jdbc:postgresql://postgresql:5432/TwentyOnePoints + - SPRING_ELASTICSEARCH_URIS=http://elasticsearch:9200 ports: - 127.0.0.1:8080:8080 - twentyonepoints-postgresql: - image: postgres:14.5 - # volumes: - # - ~/volumes/jhipster/TwentyOnePoints/postgresql/:/var/lib/postgresql/data/ - environment: - - POSTGRES_USER=TwentyOnePoints - - POSTGRES_PASSWORD= - - POSTGRES_HOST_AUTH_METHOD=trust - # If you want to expose these ports outside your dev PC, - # remove the "127.0.0.1:" prefix - ports: - - 127.0.0.1:5432:5432 - twentyonepoints-elasticsearch: - image: docker.elastic.co/elasticsearch/elasticsearch:7.17.4 - # volumes: - # - ~/volumes/jhipster/TwentyOnePoints/elasticsearch/:/usr/share/elasticsearch/data/ - # If you want to expose these ports outside your dev PC, - # remove the "127.0.0.1:" prefix - ports: - - 127.0.0.1:9200:9200 - - 127.0.0.1:9300:9300 - environment: - - 'ES_JAVA_OPTS=-Xms256m -Xmx256m' - - 'discovery.type=single-node' + healthcheck: + test: + - CMD + - curl + - -f + - http://localhost:8080/management/health + interval: 5s + timeout: 5s + retries: 40 + depends_on: + postgresql: + condition: service_healthy + elasticsearch: + condition: service_healthy + postgresql: + extends: + file: ./postgresql.yml + service: postgresql + elasticsearch: + extends: + file: ./elasticsearch.yml + service: elasticsearch diff --git a/src/main/docker/central-server-config/README.md b/src/main/docker/central-server-config/README.md deleted file mode 100644 index c3e9cfd5..00000000 --- a/src/main/docker/central-server-config/README.md +++ /dev/null @@ -1 +0,0 @@ -# Central configuration sources details diff --git a/src/main/docker/elasticsearch.yml b/src/main/docker/elasticsearch.yml index e82df628..0c9c75f3 100644 --- a/src/main/docker/elasticsearch.yml +++ b/src/main/docker/elasticsearch.yml @@ -1,8 +1,8 @@ # This configuration is intended for development purpose, it's **your** responsibility to harden it for production -version: '3.8' +name: twentyonepoints services: - twentyonepoints-elasticsearch: - image: docker.elastic.co/elasticsearch/elasticsearch:7.17.4 + elasticsearch: + image: docker.elastic.co/elasticsearch/elasticsearch:8.10.4 # volumes: # - ~/volumes/jhipster/TwentyOnePoints/elasticsearch/:/usr/share/elasticsearch/data/ # If you want to expose these ports outside your dev PC, @@ -11,16 +11,11 @@ services: - 127.0.0.1:9200:9200 - 127.0.0.1:9300:9300 environment: - - 'xpack.security.enabled=false' + - 'ES_JAVA_OPTS=-Xms256m -Xmx256m' - 'discovery.type=single-node' - - 'bootstrap.memory_lock=true' - - 'ES_JAVA_OPTS=-Xms512m -Xmx512m' - ulimits: - memlock: - soft: -1 - hard: -1 - nofile: - soft: 65536 - hard: 65536 - cap_add: - - IPC_LOCK + - 'xpack.security.enabled=false' + healthcheck: + test: ['CMD', 'curl', '-f', 'http://localhost:9200/_cluster/health?wait_for_status=green&timeout=10s'] + interval: 5s + timeout: 10s + retries: 10 diff --git a/src/main/docker/jhipster-control-center.yml b/src/main/docker/jhipster-control-center.yml index 22fa07c1..2bfbf33b 100644 --- a/src/main/docker/jhipster-control-center.yml +++ b/src/main/docker/jhipster-control-center.yml @@ -28,7 +28,7 @@ # - In Consul mode, the ports are in the consul.yml file. # - In Eureka mode, the ports are in the jhipster-registry.yml file. -version: '3.8' +name: twentyonepoints services: jhipster-control-center: image: 'jhipster/jhipster-control-center:v0.5.0' @@ -40,7 +40,6 @@ services: environment: - _JAVA_OPTIONS=-Xmx512m -Xms256m - SPRING_PROFILES_ACTIVE=prod,api-docs,static - - JHIPSTER_SLEEP=30 # gives time for other services to boot before the application - SPRING_SECURITY_USER_PASSWORD=admin # The token should have the same value than the one declared in you Spring configuration under the jhipster.security.authentication.jwt.base64-secret configuration's entry - JHIPSTER_SECURITY_AUTHENTICATION_JWT_BASE64_SECRET=YjAwZDU2ODI3NzNjZjk0NjVhY2UzNGViZmY0YjY1ZGY2MDI5MTc5Y2FlZWYxNzE5Yjg5ZjMxOGZhZTgwZjA5NTFkN2VmNDM3ZGMyZDNjZTViNDAwYTg4NmNlMmM2ZDkxZTMwMDk5YmUxNWNkZmE0YTNiNDlhMGNhZmM4OTk1NmQ= diff --git a/src/main/docker/monitoring.yml b/src/main/docker/monitoring.yml index 1786b152..f767526d 100644 --- a/src/main/docker/monitoring.yml +++ b/src/main/docker/monitoring.yml @@ -1,8 +1,8 @@ # This configuration is intended for development purpose, it's **your** responsibility to harden it for production -version: '3.8' +name: twentyonepoints services: - twentyonepoints-prometheus: - image: prom/prometheus:v2.38.0 + prometheus: + image: prom/prometheus:v2.51.2 volumes: - ./prometheus/:/etc/prometheus/ command: @@ -14,8 +14,8 @@ services: # On MacOS, remove next line and replace localhost by host.docker.internal in prometheus/prometheus.yml and # grafana/provisioning/datasources/datasource.yml network_mode: 'host' # to test locally running service - twentyonepoints-grafana: - image: grafana/grafana:9.1.0 + grafana: + image: grafana/grafana:10.4.2 volumes: - ./grafana/provisioning/:/etc/grafana/provisioning/ environment: diff --git a/src/main/docker/postgresql.yml b/src/main/docker/postgresql.yml index 4d2d7bc0..7d35095c 100644 --- a/src/main/docker/postgresql.yml +++ b/src/main/docker/postgresql.yml @@ -1,14 +1,19 @@ # This configuration is intended for development purpose, it's **your** responsibility to harden it for production -version: '3.8' +name: twentyonepoints services: - twentyonepoints-postgresql: - image: postgres:14.5 + postgresql: + image: postgres:16.2 # volumes: # - ~/volumes/jhipster/TwentyOnePoints/postgresql/:/var/lib/postgresql/data/ environment: - POSTGRES_USER=TwentyOnePoints - POSTGRES_PASSWORD= - POSTGRES_HOST_AUTH_METHOD=trust + healthcheck: + test: ['CMD-SHELL', 'pg_isready -U $${POSTGRES_USER}'] + interval: 5s + timeout: 5s + retries: 10 # If you want to expose these ports outside your dev PC, # remove the "127.0.0.1:" prefix ports: diff --git a/src/main/docker/services.yml b/src/main/docker/services.yml new file mode 100644 index 00000000..2baac2ae --- /dev/null +++ b/src/main/docker/services.yml @@ -0,0 +1,11 @@ +# This configuration is intended for development purpose, it's **your** responsibility to harden it for production +name: twentyonepoints +services: + postgresql: + extends: + file: ./postgresql.yml + service: postgresql + elasticsearch: + extends: + file: ./elasticsearch.yml + service: elasticsearch diff --git a/src/main/docker/sonar.yml b/src/main/docker/sonar.yml index 58a6be3f..433bf7c8 100644 --- a/src/main/docker/sonar.yml +++ b/src/main/docker/sonar.yml @@ -1,13 +1,15 @@ # This configuration is intended for development purpose, it's **your** responsibility to harden it for production -version: '3.8' +name: twentyonepoints services: - twentyonepoints-sonar: - image: sonarqube:9.6.0-community - # Authentication is turned off for out of the box experience while trying out SonarQube - # For real use cases delete sonar.forceAuthentication variable or set sonar.forceAuthentication=true + sonar: + container_name: sonarqube + image: sonarqube:10.5.0-community + # Forced authentication redirect for UI is turned off for out of the box experience while trying out SonarQube + # For real use cases delete SONAR_FORCEAUTHENTICATION variable or set SONAR_FORCEAUTHENTICATION=true environment: - - sonar.forceAuthentication=false + - SONAR_FORCEAUTHENTICATION=false # If you want to expose these ports outside your dev PC, # remove the "127.0.0.1:" prefix ports: - 127.0.0.1:9001:9000 + - 127.0.0.1:9000:9000 diff --git a/src/main/docker/zipkin.yml b/src/main/docker/zipkin.yml deleted file mode 100644 index 07938b9e..00000000 --- a/src/main/docker/zipkin.yml +++ /dev/null @@ -1,7 +0,0 @@ -# This configuration is intended for development purpose, it's **your** responsibility to harden it for production -version: '3.8' -services: - zipkin: - image: openzipkin/zipkin:2.23 - ports: - - 127.0.0.1:9411:9411 diff --git a/src/main/java/org/jhipster/health/GeneratedByJHipster.java b/src/main/java/org/jhipster/health/GeneratedByJHipster.java index 18fa99f5..4eb04e54 100644 --- a/src/main/java/org/jhipster/health/GeneratedByJHipster.java +++ b/src/main/java/org/jhipster/health/GeneratedByJHipster.java @@ -1,12 +1,12 @@ package org.jhipster.health; +import jakarta.annotation.Generated; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -import javax.annotation.Generated; -@Generated(value = "JHipster", comments = "Generated by JHipster 7.9.3") +@Generated(value = "JHipster", comments = "Generated by JHipster 8.4.0") @Retention(RetentionPolicy.SOURCE) @Target({ ElementType.TYPE }) public @interface GeneratedByJHipster { diff --git a/src/main/java/org/jhipster/health/TwentyOnePointsApp.java b/src/main/java/org/jhipster/health/TwentyOnePointsApp.java index 7399891b..2072ac54 100644 --- a/src/main/java/org/jhipster/health/TwentyOnePointsApp.java +++ b/src/main/java/org/jhipster/health/TwentyOnePointsApp.java @@ -1,11 +1,11 @@ package org.jhipster.health; +import jakarta.annotation.PostConstruct; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Arrays; import java.util.Collection; import java.util.Optional; -import javax.annotation.PostConstruct; import org.apache.commons.lang3.StringUtils; import org.jhipster.health.config.ApplicationProperties; import org.jhipster.health.config.CRLFLogConverter; @@ -73,6 +73,7 @@ public static void main(String[] args) { private static void logApplicationStartup(Environment env) { String protocol = Optional.ofNullable(env.getProperty("server.ssl.key-store")).map(key -> "https").orElse("http"); + String applicationName = env.getProperty("spring.application.name"); String serverPort = env.getProperty("server.port"); String contextPath = Optional.ofNullable(env.getProperty("server.servlet.context-path")) .filter(StringUtils::isNotBlank) @@ -85,12 +86,15 @@ private static void logApplicationStartup(Environment env) { } log.info( CRLFLogConverter.CRLF_SAFE_MARKER, - "\n----------------------------------------------------------\n\t" + - "Application '{}' is running! Access URLs:\n\t" + - "Local: \t\t{}://localhost:{}{}\n\t" + - "External: \t{}://{}:{}{}\n\t" + - "Profile(s): \t{}\n----------------------------------------------------------", - env.getProperty("spring.application.name"), + """ + + ---------------------------------------------------------- + \tApplication '{}' is running! Access URLs: + \tLocal: \t\t{}://localhost:{}{} + \tExternal: \t{}://{}:{}{} + \tProfile(s): \t{} + ----------------------------------------------------------""", + applicationName, protocol, serverPort, contextPath, diff --git a/src/main/java/org/jhipster/health/aop/logging/LoggingAspect.java b/src/main/java/org/jhipster/health/aop/logging/LoggingAspect.java index c6e2ba07..4bfad5c4 100644 --- a/src/main/java/org/jhipster/health/aop/logging/LoggingAspect.java +++ b/src/main/java/org/jhipster/health/aop/logging/LoggingAspect.java @@ -81,7 +81,7 @@ public void logAfterThrowing(JoinPoint joinPoint, Throwable e) { logger(joinPoint).error( "Exception in {}() with cause = {}", joinPoint.getSignature().getName(), - e.getCause() != null ? e.getCause() : "NULL" + e.getCause() != null ? String.valueOf(e.getCause()) : "NULL" ); } } diff --git a/src/main/java/org/jhipster/health/aop/logging/package-info.java b/src/main/java/org/jhipster/health/aop/logging/package-info.java new file mode 100644 index 00000000..ac44359d --- /dev/null +++ b/src/main/java/org/jhipster/health/aop/logging/package-info.java @@ -0,0 +1,4 @@ +/** + * Logging aspect. + */ +package org.jhipster.health.aop.logging; diff --git a/src/main/java/org/jhipster/health/config/ApplicationProperties.java b/src/main/java/org/jhipster/health/config/ApplicationProperties.java index 91ae4733..b428cf04 100644 --- a/src/main/java/org/jhipster/health/config/ApplicationProperties.java +++ b/src/main/java/org/jhipster/health/config/ApplicationProperties.java @@ -3,7 +3,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties; /** - * Properties specific to Android Software Portal. + * Properties specific to Twenty One Points. *

* We use the {@link Async} annotation to send emails asynchronously. */ @@ -53,6 +53,10 @@ public MailService( @Async public void sendEmail(String to, String subject, String content, boolean isMultipart, boolean isHtml) { + this.sendEmailSync(to, subject, content, isMultipart, isHtml); + } + + private void sendEmailSync(String to, String subject, String content, boolean isMultipart, boolean isHtml) { log.debug( "Send email[multipart '{}' and html '{}'] to '{}' with subject '{}' and content={}", isMultipart, @@ -79,6 +83,10 @@ public void sendEmail(String to, String subject, String content, boolean isMulti @Async public void sendEmailFromTemplate(User user, String templateName, String titleKey) { + this.sendEmailFromTemplateSync(user, templateName, titleKey); + } + + private void sendEmailFromTemplateSync(User user, String templateName, String titleKey) { if (user.getEmail() == null) { log.debug("Email doesn't exist for user '{}'", user.getLogin()); return; @@ -89,24 +97,24 @@ public void sendEmailFromTemplate(User user, String templateName, String titleKe context.setVariable(BASE_URL, jHipsterProperties.getMail().getBaseUrl()); String content = templateEngine.process(templateName, context); String subject = messageSource.getMessage(titleKey, null, locale); - sendEmail(user.getEmail(), subject, content, false, true); + this.sendEmailSync(user.getEmail(), subject, content, false, true); } @Async public void sendActivationEmail(User user) { log.debug("Sending activation email to '{}'", user.getEmail()); - sendEmailFromTemplate(user, "mail/activationEmail", "email.activation.title"); + this.sendEmailFromTemplateSync(user, "mail/activationEmail", "email.activation.title"); } @Async public void sendCreationEmail(User user) { log.debug("Sending creation email to '{}'", user.getEmail()); - sendEmailFromTemplate(user, "mail/creationEmail", "email.activation.title"); + this.sendEmailFromTemplateSync(user, "mail/creationEmail", "email.activation.title"); } @Async public void sendPasswordResetMail(User user) { log.debug("Sending password reset email to '{}'", user.getEmail()); - sendEmailFromTemplate(user, "mail/passwordResetEmail", "email.reset.title"); + this.sendEmailFromTemplateSync(user, "mail/passwordResetEmail", "email.reset.title"); } } diff --git a/src/main/java/org/jhipster/health/service/UserService.java b/src/main/java/org/jhipster/health/service/UserService.java index c7d356e9..28387ed5 100644 --- a/src/main/java/org/jhipster/health/service/UserService.java +++ b/src/main/java/org/jhipster/health/service/UserService.java @@ -182,7 +182,7 @@ public User createUser(AdminUserDTO userDTO) { user.setAuthorities(authorities); } userRepository.save(user); - userSearchRepository.save(user); + userSearchRepository.index(user); this.clearUserCaches(user); log.debug("Created Information for User: {}", user); return user; @@ -218,7 +218,8 @@ public Optional updateUser(AdminUserDTO userDTO) { .filter(Optional::isPresent) .map(Optional::get) .forEach(managedAuthorities::add); - userSearchRepository.save(user); + userRepository.save(user); + userSearchRepository.index(user); this.clearUserCaches(user); log.debug("Changed Information for User: {}", user); return user; @@ -231,7 +232,7 @@ public void deleteUser(String login) { .findOneByLogin(login) .ifPresent(user -> { userRepository.delete(user); - userSearchRepository.delete(user); + userSearchRepository.deleteFromIndex(user); this.clearUserCaches(user); log.debug("Deleted User: {}", user); }); @@ -257,7 +258,8 @@ public void updateUser(String firstName, String lastName, String email, String l } user.setLangKey(langKey); user.setImageUrl(imageUrl); - userSearchRepository.save(user); + userRepository.save(user); + userSearchRepository.index(user); this.clearUserCaches(user); log.debug("Changed Information for User: {}", user); }); @@ -311,7 +313,7 @@ public void removeNotActivatedUsers() { .forEach(user -> { log.debug("Deleting not activated user {}", user.getLogin()); userRepository.delete(user); - userSearchRepository.delete(user); + userSearchRepository.deleteFromIndex(user); this.clearUserCaches(user); }); } @@ -322,7 +324,7 @@ public void removeNotActivatedUsers() { */ @Transactional(readOnly = true) public List getAuthorities() { - return authorityRepository.findAll().stream().map(Authority::getName).collect(Collectors.toList()); + return authorityRepository.findAll().stream().map(Authority::getName).toList(); } private void clearUserCaches(User user) { diff --git a/src/main/java/org/jhipster/health/service/dto/AdminUserDTO.java b/src/main/java/org/jhipster/health/service/dto/AdminUserDTO.java index b457662e..00ee170f 100644 --- a/src/main/java/org/jhipster/health/service/dto/AdminUserDTO.java +++ b/src/main/java/org/jhipster/health/service/dto/AdminUserDTO.java @@ -1,10 +1,10 @@ package org.jhipster.health.service.dto; +import jakarta.validation.constraints.*; import java.io.Serializable; import java.time.Instant; import java.util.Set; import java.util.stream.Collectors; -import javax.validation.constraints.*; import org.jhipster.health.config.Constants; import org.jhipster.health.domain.Authority; import org.jhipster.health.domain.User; diff --git a/src/main/java/org/jhipster/health/service/dto/package-info.java b/src/main/java/org/jhipster/health/service/dto/package-info.java index 5f15b131..c7380ce5 100644 --- a/src/main/java/org/jhipster/health/service/dto/package-info.java +++ b/src/main/java/org/jhipster/health/service/dto/package-info.java @@ -1,4 +1,4 @@ /** - * Data Transfer Objects. + * Data transfer objects for rest mapping. */ package org.jhipster.health.service.dto; diff --git a/src/main/java/org/jhipster/health/service/mapper/UserMapper.java b/src/main/java/org/jhipster/health/service/mapper/UserMapper.java index 1d75c34c..0a8c46ae 100644 --- a/src/main/java/org/jhipster/health/service/mapper/UserMapper.java +++ b/src/main/java/org/jhipster/health/service/mapper/UserMapper.java @@ -21,7 +21,7 @@ public class UserMapper { public List usersToUserDTOs(List users) { - return users.stream().filter(Objects::nonNull).map(this::userToUserDTO).collect(Collectors.toList()); + return users.stream().filter(Objects::nonNull).map(this::userToUserDTO).toList(); } public UserDTO userToUserDTO(User user) { @@ -29,7 +29,7 @@ public UserDTO userToUserDTO(User user) { } public List usersToAdminUserDTOs(List users) { - return users.stream().filter(Objects::nonNull).map(this::userToAdminUserDTO).collect(Collectors.toList()); + return users.stream().filter(Objects::nonNull).map(this::userToAdminUserDTO).toList(); } public AdminUserDTO userToAdminUserDTO(User user) { @@ -37,7 +37,7 @@ public AdminUserDTO userToAdminUserDTO(User user) { } public List userDTOsToUsers(List userDTOs) { - return userDTOs.stream().filter(Objects::nonNull).map(this::userDTOToUser).collect(Collectors.toList()); + return userDTOs.stream().filter(Objects::nonNull).map(this::userDTOToUser).toList(); } public User userDTOToUser(AdminUserDTO userDTO) { diff --git a/src/main/java/org/jhipster/health/service/mapper/package-info.java b/src/main/java/org/jhipster/health/service/mapper/package-info.java index b971ff47..c06969c8 100644 --- a/src/main/java/org/jhipster/health/service/mapper/package-info.java +++ b/src/main/java/org/jhipster/health/service/mapper/package-info.java @@ -1,4 +1,4 @@ /** - * MapStruct mappers for mapping domain objects and Data Transfer Objects. + * Data transfer objects mappers. */ package org.jhipster.health.service.mapper; diff --git a/src/main/java/org/jhipster/health/service/package-info.java b/src/main/java/org/jhipster/health/service/package-info.java index 8aa1c8a5..37278afc 100644 --- a/src/main/java/org/jhipster/health/service/package-info.java +++ b/src/main/java/org/jhipster/health/service/package-info.java @@ -1,4 +1,4 @@ /** - * Service layer beans. + * Service layer. */ package org.jhipster.health.service; diff --git a/src/main/java/org/jhipster/health/web/filter/SpaWebFilter.java b/src/main/java/org/jhipster/health/web/filter/SpaWebFilter.java new file mode 100644 index 00000000..7b1dd471 --- /dev/null +++ b/src/main/java/org/jhipster/health/web/filter/SpaWebFilter.java @@ -0,0 +1,34 @@ +package org.jhipster.health.web.filter; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; +import org.springframework.web.filter.OncePerRequestFilter; + +public class SpaWebFilter extends OncePerRequestFilter { + + /** + * Forwards any unmapped paths (except those containing a period) to the client {@code index.html}. + */ + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) + throws ServletException, IOException { + // Request URI includes the contextPath if any, removed it. + String path = request.getRequestURI().substring(request.getContextPath().length()); + if ( + !path.startsWith("/api") && + !path.startsWith("/management") && + !path.startsWith("/v3/api-docs") && + !path.startsWith("/h2-console") && + !path.contains(".") && + path.matches("/(.*)") + ) { + request.getRequestDispatcher("/index.html").forward(request, response); + return; + } + + filterChain.doFilter(request, response); + } +} diff --git a/src/main/java/org/jhipster/health/web/filter/package-info.java b/src/main/java/org/jhipster/health/web/filter/package-info.java new file mode 100644 index 00000000..bc73f239 --- /dev/null +++ b/src/main/java/org/jhipster/health/web/filter/package-info.java @@ -0,0 +1,4 @@ +/** + * Request chain filters. + */ +package org.jhipster.health.web.filter; diff --git a/src/main/java/org/jhipster/health/web/rest/AccountResource.java b/src/main/java/org/jhipster/health/web/rest/AccountResource.java index 613d0ab2..c8ac4b28 100644 --- a/src/main/java/org/jhipster/health/web/rest/AccountResource.java +++ b/src/main/java/org/jhipster/health/web/rest/AccountResource.java @@ -1,8 +1,7 @@ package org.jhipster.health.web.rest; +import jakarta.validation.Valid; import java.util.*; -import javax.servlet.http.HttpServletRequest; -import javax.validation.Valid; import org.apache.commons.lang3.StringUtils; import org.jhipster.health.domain.User; import org.jhipster.health.repository.UserRepository; @@ -79,18 +78,6 @@ public void activateAccount(@RequestParam(value = "key") String key) { } } - /** - * {@code GET /authenticate} : check if the user is authenticated, and return its login. - * - * @param request the HTTP request. - * @return the login if the user is authenticated. - */ - @GetMapping("/authenticate") - public String isAuthenticated(HttpServletRequest request) { - log.debug("REST request to check if the current user is authenticated"); - return request.getRemoteUser(); - } - /** * {@code GET /account} : get the current user. * @@ -117,7 +104,7 @@ public void saveAccount(@Valid @RequestBody AdminUserDTO userDTO) { String userLogin = SecurityUtils.getCurrentUserLogin() .orElseThrow(() -> new AccountResourceException("Current user login not found")); Optional existingUser = userRepository.findOneByEmailIgnoreCase(userDTO.getEmail()); - if (existingUser.isPresent() && (!existingUser.get().getLogin().equalsIgnoreCase(userLogin))) { + if (existingUser.isPresent() && (!existingUser.orElseThrow().getLogin().equalsIgnoreCase(userLogin))) { throw new EmailAlreadyUsedException(); } Optional user = userRepository.findOneByLogin(userLogin); @@ -156,7 +143,7 @@ public void changePassword(@RequestBody PasswordChangeDTO passwordChangeDto) { public void requestPasswordReset(@RequestBody String mail) { Optional user = userService.requestPasswordReset(mail); if (user.isPresent()) { - mailService.sendPasswordResetMail(user.get()); + mailService.sendPasswordResetMail(user.orElseThrow()); } else { // Pretend the request has been successful to prevent checking which emails really exist // but log that an invalid attempt has been made diff --git a/src/main/java/org/jhipster/health/web/rest/AuthenticateController.java b/src/main/java/org/jhipster/health/web/rest/AuthenticateController.java new file mode 100644 index 00000000..5c0458c0 --- /dev/null +++ b/src/main/java/org/jhipster/health/web/rest/AuthenticateController.java @@ -0,0 +1,124 @@ +package org.jhipster.health.web.rest; + +import static org.jhipster.health.security.SecurityUtils.AUTHORITIES_KEY; +import static org.jhipster.health.security.SecurityUtils.JWT_ALGORITHM; + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.Valid; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.stream.Collectors; +import org.jhipster.health.web.rest.vm.LoginVM; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.jwt.JwsHeader; +import org.springframework.security.oauth2.jwt.JwtClaimsSet; +import org.springframework.security.oauth2.jwt.JwtEncoder; +import org.springframework.security.oauth2.jwt.JwtEncoderParameters; +import org.springframework.web.bind.annotation.*; + +/** + * Controller to authenticate users. + */ +@RestController +@RequestMapping("/api") +public class AuthenticateController { + + private final Logger log = LoggerFactory.getLogger(AuthenticateController.class); + + private final JwtEncoder jwtEncoder; + + @Value("${jhipster.security.authentication.jwt.token-validity-in-seconds:0}") + private long tokenValidityInSeconds; + + @Value("${jhipster.security.authentication.jwt.token-validity-in-seconds-for-remember-me:0}") + private long tokenValidityInSecondsForRememberMe; + + private final AuthenticationManagerBuilder authenticationManagerBuilder; + + public AuthenticateController(JwtEncoder jwtEncoder, AuthenticationManagerBuilder authenticationManagerBuilder) { + this.jwtEncoder = jwtEncoder; + this.authenticationManagerBuilder = authenticationManagerBuilder; + } + + @PostMapping("/authenticate") + public ResponseEntity authorize(@Valid @RequestBody LoginVM loginVM) { + UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken( + loginVM.getUsername(), + loginVM.getPassword() + ); + + Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken); + SecurityContextHolder.getContext().setAuthentication(authentication); + String jwt = this.createToken(authentication, loginVM.isRememberMe()); + HttpHeaders httpHeaders = new HttpHeaders(); + httpHeaders.setBearerAuth(jwt); + return new ResponseEntity<>(new JWTToken(jwt), httpHeaders, HttpStatus.OK); + } + + /** + * {@code GET /authenticate} : check if the user is authenticated, and return its login. + * + * @param request the HTTP request. + * @return the login if the user is authenticated. + */ + @GetMapping("/authenticate") + public String isAuthenticated(HttpServletRequest request) { + log.debug("REST request to check if the current user is authenticated"); + return request.getRemoteUser(); + } + + public String createToken(Authentication authentication, boolean rememberMe) { + String authorities = authentication.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.joining(" ")); + + Instant now = Instant.now(); + Instant validity; + if (rememberMe) { + validity = now.plus(this.tokenValidityInSecondsForRememberMe, ChronoUnit.SECONDS); + } else { + validity = now.plus(this.tokenValidityInSeconds, ChronoUnit.SECONDS); + } + + // @formatter:off + JwtClaimsSet claims = JwtClaimsSet.builder() + .issuedAt(now) + .expiresAt(validity) + .subject(authentication.getName()) + .claim(AUTHORITIES_KEY, authorities) + .build(); + + JwsHeader jwsHeader = JwsHeader.with(JWT_ALGORITHM).build(); + return this.jwtEncoder.encode(JwtEncoderParameters.from(jwsHeader, claims)).getTokenValue(); + } + + /** + * Object to return as body in JWT Authentication. + */ + static class JWTToken { + + private String idToken; + + JWTToken(String idToken) { + this.idToken = idToken; + } + + @JsonProperty("id_token") + String getIdToken() { + return idToken; + } + + void setIdToken(String idToken) { + this.idToken = idToken; + } + } +} diff --git a/src/main/java/org/jhipster/health/web/rest/AuthorityResource.java b/src/main/java/org/jhipster/health/web/rest/AuthorityResource.java new file mode 100644 index 00000000..95090a57 --- /dev/null +++ b/src/main/java/org/jhipster/health/web/rest/AuthorityResource.java @@ -0,0 +1,101 @@ +package org.jhipster.health.web.rest; + +import jakarta.validation.Valid; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.List; +import java.util.Optional; +import org.jhipster.health.domain.Authority; +import org.jhipster.health.repository.AuthorityRepository; +import org.jhipster.health.web.rest.errors.BadRequestAlertException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.*; +import tech.jhipster.web.util.HeaderUtil; +import tech.jhipster.web.util.ResponseUtil; + +/** + * REST controller for managing {@link org.jhipster.health.domain.Authority}. + */ +@RestController +@RequestMapping("/api/authorities") +@Transactional +public class AuthorityResource { + + private final Logger log = LoggerFactory.getLogger(AuthorityResource.class); + + private static final String ENTITY_NAME = "adminAuthority"; + + @Value("${jhipster.clientApp.name}") + private String applicationName; + + private final AuthorityRepository authorityRepository; + + public AuthorityResource(AuthorityRepository authorityRepository) { + this.authorityRepository = authorityRepository; + } + + /** + * {@code POST /authorities} : Create a new authority. + * + * @param authority the authority to create. + * @return the {@link ResponseEntity} with status {@code 201 (Created)} and with body the new authority, or with status {@code 400 (Bad Request)} if the authority has already an ID. + * @throws URISyntaxException if the Location URI syntax is incorrect. + */ + @PostMapping("") + @PreAuthorize("hasAnyAuthority('ROLE_ADMIN')") + public ResponseEntity createAuthority(@Valid @RequestBody Authority authority) throws URISyntaxException { + log.debug("REST request to save Authority : {}", authority); + if (authorityRepository.existsById(authority.getName())) { + throw new BadRequestAlertException("authority already exists", ENTITY_NAME, "idexists"); + } + authority = authorityRepository.save(authority); + return ResponseEntity.created(new URI("/api/authorities/" + authority.getName())) + .headers(HeaderUtil.createEntityCreationAlert(applicationName, true, ENTITY_NAME, authority.getName())) + .body(authority); + } + + /** + * {@code GET /authorities} : get all the authorities. + * + * @return the {@link ResponseEntity} with status {@code 200 (OK)} and the list of authorities in body. + */ + @GetMapping("") + @PreAuthorize("hasAnyAuthority('ROLE_ADMIN')") + public List getAllAuthorities() { + log.debug("REST request to get all Authorities"); + return authorityRepository.findAll(); + } + + /** + * {@code GET /authorities/:id} : get the "id" authority. + * + * @param id the id of the authority to retrieve. + * @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body the authority, or with status {@code 404 (Not Found)}. + */ + @GetMapping("/{id}") + @PreAuthorize("hasAnyAuthority('ROLE_ADMIN')") + public ResponseEntity getAuthority(@PathVariable("id") String id) { + log.debug("REST request to get Authority : {}", id); + Optional authority = authorityRepository.findById(id); + return ResponseUtil.wrapOrNotFound(authority); + } + + /** + * {@code DELETE /authorities/:id} : delete the "id" authority. + * + * @param id the id of the authority to delete. + * @return the {@link ResponseEntity} with status {@code 204 (NO_CONTENT)}. + */ + @DeleteMapping("/{id}") + @PreAuthorize("hasAnyAuthority('ROLE_ADMIN')") + public ResponseEntity deleteAuthority(@PathVariable("id") String id) { + log.debug("REST request to delete Authority : {}", id); + authorityRepository.deleteById(id); + return ResponseEntity.noContent().headers(HeaderUtil.createEntityDeletionAlert(applicationName, true, ENTITY_NAME, id)).build(); + } +} diff --git a/src/main/java/org/jhipster/health/web/rest/BloodPressureResource.java b/src/main/java/org/jhipster/health/web/rest/BloodPressureResource.java index 9b8e4fb9..9b651b92 100644 --- a/src/main/java/org/jhipster/health/web/rest/BloodPressureResource.java +++ b/src/main/java/org/jhipster/health/web/rest/BloodPressureResource.java @@ -1,18 +1,17 @@ package org.jhipster.health.web.rest; -import static org.elasticsearch.index.query.QueryBuilders.*; - +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; import java.net.URI; import java.net.URISyntaxException; import java.util.List; import java.util.Objects; import java.util.Optional; -import javax.validation.Valid; -import javax.validation.constraints.NotNull; import org.jhipster.health.domain.BloodPressure; import org.jhipster.health.repository.BloodPressureRepository; import org.jhipster.health.repository.search.BloodPressureSearchRepository; import org.jhipster.health.web.rest.errors.BadRequestAlertException; +import org.jhipster.health.web.rest.errors.ElasticsearchExceptionMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; @@ -31,7 +30,7 @@ * REST controller for managing {@link org.jhipster.health.domain.BloodPressure}. */ @RestController -@RequestMapping("/api") +@RequestMapping("/api/blood-pressures") @Transactional public class BloodPressureResource { @@ -61,17 +60,17 @@ public BloodPressureResource( * @return the {@link ResponseEntity} with status {@code 201 (Created)} and with body the new bloodPressure, or with status {@code 400 (Bad Request)} if the bloodPressure has already an ID. * @throws URISyntaxException if the Location URI syntax is incorrect. */ - @PostMapping("/blood-pressures") + @PostMapping("") public ResponseEntity createBloodPressure(@Valid @RequestBody BloodPressure bloodPressure) throws URISyntaxException { log.debug("REST request to save BloodPressure : {}", bloodPressure); if (bloodPressure.getId() != null) { throw new BadRequestAlertException("A new bloodPressure cannot already have an ID", ENTITY_NAME, "idexists"); } - BloodPressure result = bloodPressureRepository.save(bloodPressure); - bloodPressureSearchRepository.index(result); - return ResponseEntity.created(new URI("/api/blood-pressures/" + result.getId())) - .headers(HeaderUtil.createEntityCreationAlert(applicationName, true, ENTITY_NAME, result.getId().toString())) - .body(result); + bloodPressure = bloodPressureRepository.save(bloodPressure); + bloodPressureSearchRepository.index(bloodPressure); + return ResponseEntity.created(new URI("/api/blood-pressures/" + bloodPressure.getId())) + .headers(HeaderUtil.createEntityCreationAlert(applicationName, true, ENTITY_NAME, bloodPressure.getId().toString())) + .body(bloodPressure); } /** @@ -84,7 +83,7 @@ public ResponseEntity createBloodPressure(@Valid @RequestBody Blo * or with status {@code 500 (Internal Server Error)} if the bloodPressure couldn't be updated. * @throws URISyntaxException if the Location URI syntax is incorrect. */ - @PutMapping("/blood-pressures/{id}") + @PutMapping("/{id}") public ResponseEntity updateBloodPressure( @PathVariable(value = "id", required = false) final Long id, @Valid @RequestBody BloodPressure bloodPressure @@ -101,11 +100,11 @@ public ResponseEntity updateBloodPressure( throw new BadRequestAlertException("Entity not found", ENTITY_NAME, "idnotfound"); } - BloodPressure result = bloodPressureRepository.save(bloodPressure); - bloodPressureSearchRepository.index(result); + bloodPressure = bloodPressureRepository.save(bloodPressure); + bloodPressureSearchRepository.index(bloodPressure); return ResponseEntity.ok() .headers(HeaderUtil.createEntityUpdateAlert(applicationName, true, ENTITY_NAME, bloodPressure.getId().toString())) - .body(result); + .body(bloodPressure); } /** @@ -119,7 +118,7 @@ public ResponseEntity updateBloodPressure( * or with status {@code 500 (Internal Server Error)} if the bloodPressure couldn't be updated. * @throws URISyntaxException if the Location URI syntax is incorrect. */ - @PatchMapping(value = "/blood-pressures/{id}", consumes = { "application/json", "application/merge-patch+json" }) + @PatchMapping(value = "/{id}", consumes = { "application/json", "application/merge-patch+json" }) public ResponseEntity partialUpdateBloodPressure( @PathVariable(value = "id", required = false) final Long id, @NotNull @RequestBody BloodPressure bloodPressure @@ -153,8 +152,7 @@ public ResponseEntity partialUpdateBloodPressure( }) .map(bloodPressureRepository::save) .map(savedBloodPressure -> { - bloodPressureSearchRepository.save(savedBloodPressure); - + bloodPressureSearchRepository.index(savedBloodPressure); return savedBloodPressure; }); @@ -171,10 +169,10 @@ public ResponseEntity partialUpdateBloodPressure( * @param eagerload flag to eager load entities from relationships (This is applicable for many-to-many). * @return the {@link ResponseEntity} with status {@code 200 (OK)} and the list of bloodPressures in body. */ - @GetMapping("/blood-pressures") + @GetMapping("") public ResponseEntity> getAllBloodPressures( - @org.springdoc.api.annotations.ParameterObject Pageable pageable, - @RequestParam(required = false, defaultValue = "false") boolean eagerload + @org.springdoc.core.annotations.ParameterObject Pageable pageable, + @RequestParam(name = "eagerload", required = false, defaultValue = "true") boolean eagerload ) { log.debug("REST request to get a page of BloodPressures"); Page page; @@ -193,8 +191,8 @@ public ResponseEntity> getAllBloodPressures( * @param id the id of the bloodPressure to retrieve. * @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body the bloodPressure, or with status {@code 404 (Not Found)}. */ - @GetMapping("/blood-pressures/{id}") - public ResponseEntity getBloodPressure(@PathVariable Long id) { + @GetMapping("/{id}") + public ResponseEntity getBloodPressure(@PathVariable("id") Long id) { log.debug("REST request to get BloodPressure : {}", id); Optional bloodPressure = bloodPressureRepository.findOneWithEagerRelationships(id); return ResponseUtil.wrapOrNotFound(bloodPressure); @@ -206,32 +204,36 @@ public ResponseEntity getBloodPressure(@PathVariable Long id) { * @param id the id of the bloodPressure to delete. * @return the {@link ResponseEntity} with status {@code 204 (NO_CONTENT)}. */ - @DeleteMapping("/blood-pressures/{id}") - public ResponseEntity deleteBloodPressure(@PathVariable Long id) { + @DeleteMapping("/{id}") + public ResponseEntity deleteBloodPressure(@PathVariable("id") Long id) { log.debug("REST request to delete BloodPressure : {}", id); bloodPressureRepository.deleteById(id); - bloodPressureSearchRepository.deleteById(id); + bloodPressureSearchRepository.deleteFromIndexById(id); return ResponseEntity.noContent() .headers(HeaderUtil.createEntityDeletionAlert(applicationName, true, ENTITY_NAME, id.toString())) .build(); } /** - * {@code SEARCH /_search/blood-pressures?query=:query} : search for the bloodPressure corresponding + * {@code SEARCH /blood-pressures/_search?query=:query} : search for the bloodPressure corresponding * to the query. * * @param query the query of the bloodPressure search. * @param pageable the pagination information. * @return the result of the search. */ - @GetMapping("/_search/blood-pressures") + @GetMapping("/_search") public ResponseEntity> searchBloodPressures( - @RequestParam String query, - @org.springdoc.api.annotations.ParameterObject Pageable pageable + @RequestParam("query") String query, + @org.springdoc.core.annotations.ParameterObject Pageable pageable ) { log.debug("REST request to search for a page of BloodPressures for query {}", query); - Page page = bloodPressureSearchRepository.search(query, pageable); - HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(ServletUriComponentsBuilder.fromCurrentRequest(), page); - return ResponseEntity.ok().headers(headers).body(page.getContent()); + try { + Page page = bloodPressureSearchRepository.search(query, pageable); + HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(ServletUriComponentsBuilder.fromCurrentRequest(), page); + return ResponseEntity.ok().headers(headers).body(page.getContent()); + } catch (RuntimeException e) { + throw ElasticsearchExceptionMapper.mapException(e); + } } } diff --git a/src/main/java/org/jhipster/health/web/rest/ClientForwardController.java b/src/main/java/org/jhipster/health/web/rest/ClientForwardController.java deleted file mode 100644 index 02c5b825..00000000 --- a/src/main/java/org/jhipster/health/web/rest/ClientForwardController.java +++ /dev/null @@ -1,17 +0,0 @@ -package org.jhipster.health.web.rest; - -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.GetMapping; - -@Controller -public class ClientForwardController { - - /** - * Forwards any unmapped paths (except those containing a period) to the client {@code index.html}. - * @return forward to client {@code index.html}. - */ - @GetMapping(value = "/**/{path:[^\\.]*}") - public String forward() { - return "forward:/"; - } -} diff --git a/src/main/java/org/jhipster/health/web/rest/ElasticsearchIndexResource.java b/src/main/java/org/jhipster/health/web/rest/ElasticsearchIndexResource.java deleted file mode 100644 index fa5f42a4..00000000 --- a/src/main/java/org/jhipster/health/web/rest/ElasticsearchIndexResource.java +++ /dev/null @@ -1,63 +0,0 @@ -package org.jhipster.health.web.rest; - -import com.codahale.metrics.annotation.Timed; -import java.net.URISyntaxException; -import java.util.List; -import org.jhipster.health.security.AuthoritiesConstants; -import org.jhipster.health.security.SecurityUtils; -import org.jhipster.health.service.ElasticsearchIndexService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.ResponseEntity; -import org.springframework.security.access.annotation.Secured; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; -import tech.jhipster.web.util.HeaderUtil; - -/** - * REST controller for managing Elasticsearch index. - */ -@RestController -@RequestMapping("/api") -public class ElasticsearchIndexResource { - - private final Logger log = LoggerFactory.getLogger(ElasticsearchIndexResource.class); - - private final ElasticsearchIndexService elasticsearchIndexService; - - public ElasticsearchIndexResource(ElasticsearchIndexService elasticsearchIndexService) { - this.elasticsearchIndexService = elasticsearchIndexService; - } - - @Value("${jhipster.clientApp.name}") - private String applicationName; - - /** - * POST /elasticsearch/index -> Reindex all Elasticsearch documents - */ - @PostMapping("/elasticsearch/index") - @Timed - @Secured(AuthoritiesConstants.ADMIN) - public ResponseEntity reindexAll() throws URISyntaxException { - log.info("REST request to reindex Elasticsearch by user : {}, all entities", SecurityUtils.getCurrentUserLogin()); - elasticsearchIndexService.reindexSelected(null, true); - return ResponseEntity.accepted().headers(HeaderUtil.createAlert(applicationName, "elasticsearch.reindex.accepted", "")).build(); - } - - /** - * POST /elasticsearch/selected -> Reindex selected Elasticsearch documents - */ - @PostMapping("/elasticsearch/selected") - @Timed - @Secured(AuthoritiesConstants.ADMIN) - public ResponseEntity reindexSelected(@RequestBody List selectedEntities) throws URISyntaxException { - log.info("REST request to reindex Elasticsearch by user : {}, entities: {}", SecurityUtils.getCurrentUserLogin(), selectedEntities); - elasticsearchIndexService.reindexSelected(selectedEntities, false); - return ResponseEntity.accepted() - .headers(HeaderUtil.createAlert(applicationName, "elasticsearch.reindex.acceptedSelected", "")) - .build(); - } -} diff --git a/src/main/java/org/jhipster/health/web/rest/PointsResource.java b/src/main/java/org/jhipster/health/web/rest/PointsResource.java index 94a81875..dd00d0bf 100644 --- a/src/main/java/org/jhipster/health/web/rest/PointsResource.java +++ b/src/main/java/org/jhipster/health/web/rest/PointsResource.java @@ -1,18 +1,17 @@ package org.jhipster.health.web.rest; -import static org.elasticsearch.index.query.QueryBuilders.*; - +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; import java.net.URI; import java.net.URISyntaxException; import java.util.List; import java.util.Objects; import java.util.Optional; -import javax.validation.Valid; -import javax.validation.constraints.NotNull; import org.jhipster.health.domain.Points; import org.jhipster.health.repository.PointsRepository; import org.jhipster.health.repository.search.PointsSearchRepository; import org.jhipster.health.web.rest.errors.BadRequestAlertException; +import org.jhipster.health.web.rest.errors.ElasticsearchExceptionMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; @@ -31,7 +30,7 @@ * REST controller for managing {@link org.jhipster.health.domain.Points}. */ @RestController -@RequestMapping("/api") +@RequestMapping("/api/points") @Transactional public class PointsResource { @@ -58,17 +57,17 @@ public PointsResource(PointsRepository pointsRepository, PointsSearchRepository * @return the {@link ResponseEntity} with status {@code 201 (Created)} and with body the new points, or with status {@code 400 (Bad Request)} if the points has already an ID. * @throws URISyntaxException if the Location URI syntax is incorrect. */ - @PostMapping("/points") + @PostMapping("") public ResponseEntity createPoints(@Valid @RequestBody Points points) throws URISyntaxException { log.debug("REST request to save Points : {}", points); if (points.getId() != null) { throw new BadRequestAlertException("A new points cannot already have an ID", ENTITY_NAME, "idexists"); } - Points result = pointsRepository.save(points); - pointsSearchRepository.index(result); - return ResponseEntity.created(new URI("/api/points/" + result.getId())) - .headers(HeaderUtil.createEntityCreationAlert(applicationName, true, ENTITY_NAME, result.getId().toString())) - .body(result); + points = pointsRepository.save(points); + pointsSearchRepository.index(points); + return ResponseEntity.created(new URI("/api/points/" + points.getId())) + .headers(HeaderUtil.createEntityCreationAlert(applicationName, true, ENTITY_NAME, points.getId().toString())) + .body(points); } /** @@ -81,7 +80,7 @@ public ResponseEntity createPoints(@Valid @RequestBody Points points) th * or with status {@code 500 (Internal Server Error)} if the points couldn't be updated. * @throws URISyntaxException if the Location URI syntax is incorrect. */ - @PutMapping("/points/{id}") + @PutMapping("/{id}") public ResponseEntity updatePoints( @PathVariable(value = "id", required = false) final Long id, @Valid @RequestBody Points points @@ -98,11 +97,11 @@ public ResponseEntity updatePoints( throw new BadRequestAlertException("Entity not found", ENTITY_NAME, "idnotfound"); } - Points result = pointsRepository.save(points); - pointsSearchRepository.index(result); + points = pointsRepository.save(points); + pointsSearchRepository.index(points); return ResponseEntity.ok() .headers(HeaderUtil.createEntityUpdateAlert(applicationName, true, ENTITY_NAME, points.getId().toString())) - .body(result); + .body(points); } /** @@ -116,7 +115,7 @@ public ResponseEntity updatePoints( * or with status {@code 500 (Internal Server Error)} if the points couldn't be updated. * @throws URISyntaxException if the Location URI syntax is incorrect. */ - @PatchMapping(value = "/points/{id}", consumes = { "application/json", "application/merge-patch+json" }) + @PatchMapping(value = "/{id}", consumes = { "application/json", "application/merge-patch+json" }) public ResponseEntity partialUpdatePoints( @PathVariable(value = "id", required = false) final Long id, @NotNull @RequestBody Points points @@ -156,8 +155,7 @@ public ResponseEntity partialUpdatePoints( }) .map(pointsRepository::save) .map(savedPoints -> { - pointsSearchRepository.save(savedPoints); - + pointsSearchRepository.index(savedPoints); return savedPoints; }); @@ -174,10 +172,10 @@ public ResponseEntity partialUpdatePoints( * @param eagerload flag to eager load entities from relationships (This is applicable for many-to-many). * @return the {@link ResponseEntity} with status {@code 200 (OK)} and the list of points in body. */ - @GetMapping("/points") + @GetMapping("") public ResponseEntity> getAllPoints( - @org.springdoc.api.annotations.ParameterObject Pageable pageable, - @RequestParam(required = false, defaultValue = "false") boolean eagerload + @org.springdoc.core.annotations.ParameterObject Pageable pageable, + @RequestParam(name = "eagerload", required = false, defaultValue = "true") boolean eagerload ) { log.debug("REST request to get a page of Points"); Page page; @@ -196,8 +194,8 @@ public ResponseEntity> getAllPoints( * @param id the id of the points to retrieve. * @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body the points, or with status {@code 404 (Not Found)}. */ - @GetMapping("/points/{id}") - public ResponseEntity getPoints(@PathVariable Long id) { + @GetMapping("/{id}") + public ResponseEntity getPoints(@PathVariable("id") Long id) { log.debug("REST request to get Points : {}", id); Optional points = pointsRepository.findOneWithEagerRelationships(id); return ResponseUtil.wrapOrNotFound(points); @@ -209,32 +207,36 @@ public ResponseEntity getPoints(@PathVariable Long id) { * @param id the id of the points to delete. * @return the {@link ResponseEntity} with status {@code 204 (NO_CONTENT)}. */ - @DeleteMapping("/points/{id}") - public ResponseEntity deletePoints(@PathVariable Long id) { + @DeleteMapping("/{id}") + public ResponseEntity deletePoints(@PathVariable("id") Long id) { log.debug("REST request to delete Points : {}", id); pointsRepository.deleteById(id); - pointsSearchRepository.deleteById(id); + pointsSearchRepository.deleteFromIndexById(id); return ResponseEntity.noContent() .headers(HeaderUtil.createEntityDeletionAlert(applicationName, true, ENTITY_NAME, id.toString())) .build(); } /** - * {@code SEARCH /_search/points?query=:query} : search for the points corresponding + * {@code SEARCH /points/_search?query=:query} : search for the points corresponding * to the query. * * @param query the query of the points search. * @param pageable the pagination information. * @return the result of the search. */ - @GetMapping("/_search/points") + @GetMapping("/_search") public ResponseEntity> searchPoints( - @RequestParam String query, - @org.springdoc.api.annotations.ParameterObject Pageable pageable + @RequestParam("query") String query, + @org.springdoc.core.annotations.ParameterObject Pageable pageable ) { log.debug("REST request to search for a page of Points for query {}", query); - Page page = pointsSearchRepository.search(query, pageable); - HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(ServletUriComponentsBuilder.fromCurrentRequest(), page); - return ResponseEntity.ok().headers(headers).body(page.getContent()); + try { + Page page = pointsSearchRepository.search(query, pageable); + HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(ServletUriComponentsBuilder.fromCurrentRequest(), page); + return ResponseEntity.ok().headers(headers).body(page.getContent()); + } catch (RuntimeException e) { + throw ElasticsearchExceptionMapper.mapException(e); + } } } diff --git a/src/main/java/org/jhipster/health/web/rest/PreferencesResource.java b/src/main/java/org/jhipster/health/web/rest/PreferencesResource.java index 377f3d1b..877b74b2 100644 --- a/src/main/java/org/jhipster/health/web/rest/PreferencesResource.java +++ b/src/main/java/org/jhipster/health/web/rest/PreferencesResource.java @@ -1,20 +1,18 @@ package org.jhipster.health.web.rest; -import static org.elasticsearch.index.query.QueryBuilders.*; - +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; import java.net.URI; import java.net.URISyntaxException; import java.util.List; import java.util.Objects; import java.util.Optional; -import java.util.stream.Collectors; import java.util.stream.StreamSupport; -import javax.validation.Valid; -import javax.validation.constraints.NotNull; import org.jhipster.health.domain.Preferences; import org.jhipster.health.repository.PreferencesRepository; import org.jhipster.health.repository.search.PreferencesSearchRepository; import org.jhipster.health.web.rest.errors.BadRequestAlertException; +import org.jhipster.health.web.rest.errors.ElasticsearchExceptionMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; @@ -28,7 +26,7 @@ * REST controller for managing {@link org.jhipster.health.domain.Preferences}. */ @RestController -@RequestMapping("/api") +@RequestMapping("/api/preferences") @Transactional public class PreferencesResource { @@ -55,17 +53,17 @@ public PreferencesResource(PreferencesRepository preferencesRepository, Preferen * @return the {@link ResponseEntity} with status {@code 201 (Created)} and with body the new preferences, or with status {@code 400 (Bad Request)} if the preferences has already an ID. * @throws URISyntaxException if the Location URI syntax is incorrect. */ - @PostMapping("/preferences") + @PostMapping("") public ResponseEntity createPreferences(@Valid @RequestBody Preferences preferences) throws URISyntaxException { log.debug("REST request to save Preferences : {}", preferences); if (preferences.getId() != null) { throw new BadRequestAlertException("A new preferences cannot already have an ID", ENTITY_NAME, "idexists"); } - Preferences result = preferencesRepository.save(preferences); - preferencesSearchRepository.index(result); - return ResponseEntity.created(new URI("/api/preferences/" + result.getId())) - .headers(HeaderUtil.createEntityCreationAlert(applicationName, true, ENTITY_NAME, result.getId().toString())) - .body(result); + preferences = preferencesRepository.save(preferences); + preferencesSearchRepository.index(preferences); + return ResponseEntity.created(new URI("/api/preferences/" + preferences.getId())) + .headers(HeaderUtil.createEntityCreationAlert(applicationName, true, ENTITY_NAME, preferences.getId().toString())) + .body(preferences); } /** @@ -78,7 +76,7 @@ public ResponseEntity createPreferences(@Valid @RequestBody Prefere * or with status {@code 500 (Internal Server Error)} if the preferences couldn't be updated. * @throws URISyntaxException if the Location URI syntax is incorrect. */ - @PutMapping("/preferences/{id}") + @PutMapping("/{id}") public ResponseEntity updatePreferences( @PathVariable(value = "id", required = false) final Long id, @Valid @RequestBody Preferences preferences @@ -95,11 +93,11 @@ public ResponseEntity updatePreferences( throw new BadRequestAlertException("Entity not found", ENTITY_NAME, "idnotfound"); } - Preferences result = preferencesRepository.save(preferences); - preferencesSearchRepository.index(result); + preferences = preferencesRepository.save(preferences); + preferencesSearchRepository.index(preferences); return ResponseEntity.ok() .headers(HeaderUtil.createEntityUpdateAlert(applicationName, true, ENTITY_NAME, preferences.getId().toString())) - .body(result); + .body(preferences); } /** @@ -113,7 +111,7 @@ public ResponseEntity updatePreferences( * or with status {@code 500 (Internal Server Error)} if the preferences couldn't be updated. * @throws URISyntaxException if the Location URI syntax is incorrect. */ - @PatchMapping(value = "/preferences/{id}", consumes = { "application/json", "application/merge-patch+json" }) + @PatchMapping(value = "/{id}", consumes = { "application/json", "application/merge-patch+json" }) public ResponseEntity partialUpdatePreferences( @PathVariable(value = "id", required = false) final Long id, @NotNull @RequestBody Preferences preferences @@ -144,8 +142,7 @@ public ResponseEntity partialUpdatePreferences( }) .map(preferencesRepository::save) .map(savedPreferences -> { - preferencesSearchRepository.save(savedPreferences); - + preferencesSearchRepository.index(savedPreferences); return savedPreferences; }); @@ -161,8 +158,10 @@ public ResponseEntity partialUpdatePreferences( * @param eagerload flag to eager load entities from relationships (This is applicable for many-to-many). * @return the {@link ResponseEntity} with status {@code 200 (OK)} and the list of preferences in body. */ - @GetMapping("/preferences") - public List getAllPreferences(@RequestParam(required = false, defaultValue = "false") boolean eagerload) { + @GetMapping("") + public List getAllPreferences( + @RequestParam(name = "eagerload", required = false, defaultValue = "true") boolean eagerload + ) { log.debug("REST request to get all Preferences"); if (eagerload) { return preferencesRepository.findAllWithEagerRelationships(); @@ -177,8 +176,8 @@ public List getAllPreferences(@RequestParam(required = false, defau * @param id the id of the preferences to retrieve. * @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body the preferences, or with status {@code 404 (Not Found)}. */ - @GetMapping("/preferences/{id}") - public ResponseEntity getPreferences(@PathVariable Long id) { + @GetMapping("/{id}") + public ResponseEntity getPreferences(@PathVariable("id") Long id) { log.debug("REST request to get Preferences : {}", id); Optional preferences = preferencesRepository.findOneWithEagerRelationships(id); return ResponseUtil.wrapOrNotFound(preferences); @@ -190,26 +189,30 @@ public ResponseEntity getPreferences(@PathVariable Long id) { * @param id the id of the preferences to delete. * @return the {@link ResponseEntity} with status {@code 204 (NO_CONTENT)}. */ - @DeleteMapping("/preferences/{id}") - public ResponseEntity deletePreferences(@PathVariable Long id) { + @DeleteMapping("/{id}") + public ResponseEntity deletePreferences(@PathVariable("id") Long id) { log.debug("REST request to delete Preferences : {}", id); preferencesRepository.deleteById(id); - preferencesSearchRepository.deleteById(id); + preferencesSearchRepository.deleteFromIndexById(id); return ResponseEntity.noContent() .headers(HeaderUtil.createEntityDeletionAlert(applicationName, true, ENTITY_NAME, id.toString())) .build(); } /** - * {@code SEARCH /_search/preferences?query=:query} : search for the preferences corresponding + * {@code SEARCH /preferences/_search?query=:query} : search for the preferences corresponding * to the query. * * @param query the query of the preferences search. * @return the result of the search. */ - @GetMapping("/_search/preferences") - public List searchPreferences(@RequestParam String query) { + @GetMapping("/_search") + public List searchPreferences(@RequestParam("query") String query) { log.debug("REST request to search Preferences for query {}", query); - return StreamSupport.stream(preferencesSearchRepository.search(query).spliterator(), false).collect(Collectors.toList()); + try { + return StreamSupport.stream(preferencesSearchRepository.search(query).spliterator(), false).toList(); + } catch (RuntimeException e) { + throw ElasticsearchExceptionMapper.mapException(e); + } } } diff --git a/src/main/java/org/jhipster/health/web/rest/PublicUserResource.java b/src/main/java/org/jhipster/health/web/rest/PublicUserResource.java index af661047..2e2308f6 100644 --- a/src/main/java/org/jhipster/health/web/rest/PublicUserResource.java +++ b/src/main/java/org/jhipster/health/web/rest/PublicUserResource.java @@ -1,10 +1,7 @@ package org.jhipster.health.web.rest; -import static org.elasticsearch.index.query.QueryBuilders.*; - import java.util.*; import java.util.Collections; -import java.util.stream.Collectors; import java.util.stream.StreamSupport; import org.jhipster.health.repository.search.UserSearchRepository; import org.jhipster.health.service.UserService; @@ -40,13 +37,13 @@ public PublicUserResource(UserSearchRepository userSearchRepository, UserService } /** - * {@code GET /users} : get all users with only the public informations - calling this are allowed for anyone. + * {@code GET /users} : get all users with only public information - calling this method is allowed for anyone. * * @param pageable the pagination information. * @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body all users. */ @GetMapping("/users") - public ResponseEntity> getAllPublicUsers(@org.springdoc.api.annotations.ParameterObject Pageable pageable) { + public ResponseEntity> getAllPublicUsers(@org.springdoc.core.annotations.ParameterObject Pageable pageable) { log.debug("REST request to get all public User names"); if (!onlyContainsAllowedProperties(pageable)) { return ResponseEntity.badRequest().build(); @@ -62,22 +59,13 @@ private boolean onlyContainsAllowedProperties(Pageable pageable) { } /** - * Gets a list of all roles. - * @return a string list of all roles. - */ - @GetMapping("/authorities") - public List getAuthorities() { - return userService.getAuthorities(); - } - - /** - * {@code SEARCH /_search/users/:query} : search for the User corresponding to the query. + * {@code SEARCH /users/_search/:query} : search for the User corresponding to the query. * * @param query the query to search. * @return the result of the search. */ - @GetMapping("/_search/users/{query}") - public List search(@PathVariable String query) { - return StreamSupport.stream(userSearchRepository.search(query).spliterator(), false).map(UserDTO::new).collect(Collectors.toList()); + @GetMapping("/users/_search/{query}") + public List search(@PathVariable("query") String query) { + return StreamSupport.stream(userSearchRepository.search(query).spliterator(), false).map(UserDTO::new).toList(); } } diff --git a/src/main/java/org/jhipster/health/web/rest/UserJWTController.java b/src/main/java/org/jhipster/health/web/rest/UserJWTController.java deleted file mode 100644 index 1c7e25f9..00000000 --- a/src/main/java/org/jhipster/health/web/rest/UserJWTController.java +++ /dev/null @@ -1,68 +0,0 @@ -package org.jhipster.health.web.rest; - -import com.fasterxml.jackson.annotation.JsonProperty; -import javax.validation.Valid; -import org.jhipster.health.security.jwt.JWTFilter; -import org.jhipster.health.security.jwt.TokenProvider; -import org.jhipster.health.web.rest.vm.LoginVM; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.web.bind.annotation.*; - -/** - * Controller to authenticate users. - */ -@RestController -@RequestMapping("/api") -public class UserJWTController { - - private final TokenProvider tokenProvider; - - private final AuthenticationManagerBuilder authenticationManagerBuilder; - - public UserJWTController(TokenProvider tokenProvider, AuthenticationManagerBuilder authenticationManagerBuilder) { - this.tokenProvider = tokenProvider; - this.authenticationManagerBuilder = authenticationManagerBuilder; - } - - @PostMapping("/authenticate") - public ResponseEntity authorize(@Valid @RequestBody LoginVM loginVM) { - UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken( - loginVM.getUsername(), - loginVM.getPassword() - ); - - Authentication authentication = authenticationManagerBuilder.getObject().authenticate(authenticationToken); - SecurityContextHolder.getContext().setAuthentication(authentication); - String jwt = tokenProvider.createToken(authentication, loginVM.isRememberMe()); - HttpHeaders httpHeaders = new HttpHeaders(); - httpHeaders.add(JWTFilter.AUTHORIZATION_HEADER, "Bearer " + jwt); - return new ResponseEntity<>(new JWTToken(jwt), httpHeaders, HttpStatus.OK); - } - - /** - * Object to return as body in JWT Authentication. - */ - static class JWTToken { - - private String idToken; - - JWTToken(String idToken) { - this.idToken = idToken; - } - - @JsonProperty("id_token") - String getIdToken() { - return idToken; - } - - void setIdToken(String idToken) { - this.idToken = idToken; - } - } -} diff --git a/src/main/java/org/jhipster/health/web/rest/UserResource.java b/src/main/java/org/jhipster/health/web/rest/UserResource.java index 389e50d1..8660c9a5 100644 --- a/src/main/java/org/jhipster/health/web/rest/UserResource.java +++ b/src/main/java/org/jhipster/health/web/rest/UserResource.java @@ -1,13 +1,11 @@ package org.jhipster.health.web.rest; -import static org.elasticsearch.index.query.QueryBuilders.*; - +import jakarta.validation.Valid; +import jakarta.validation.constraints.Pattern; import java.net.URI; import java.net.URISyntaxException; import java.util.*; import java.util.Collections; -import javax.validation.Valid; -import javax.validation.constraints.Pattern; import org.jhipster.health.config.Constants; import org.jhipster.health.domain.User; import org.jhipster.health.repository.UserRepository; @@ -136,16 +134,19 @@ public ResponseEntity createUser(@Valid @RequestBody AdminUserDTO userDTO) * @throws EmailAlreadyUsedException {@code 400 (Bad Request)} if the email is already in use. * @throws LoginAlreadyUsedException {@code 400 (Bad Request)} if the login is already in use. */ - @PutMapping("/users") + @PutMapping({ "/users", "/users/{login}" }) @PreAuthorize("hasAuthority(\"" + AuthoritiesConstants.ADMIN + "\")") - public ResponseEntity updateUser(@Valid @RequestBody AdminUserDTO userDTO) { + public ResponseEntity updateUser( + @PathVariable(name = "login", required = false) @Pattern(regexp = Constants.LOGIN_REGEX) String login, + @Valid @RequestBody AdminUserDTO userDTO + ) { log.debug("REST request to update User : {}", userDTO); Optional existingUser = userRepository.findOneByEmailIgnoreCase(userDTO.getEmail()); - if (existingUser.isPresent() && (!existingUser.get().getId().equals(userDTO.getId()))) { + if (existingUser.isPresent() && (!existingUser.orElseThrow().getId().equals(userDTO.getId()))) { throw new EmailAlreadyUsedException(); } existingUser = userRepository.findOneByLogin(userDTO.getLogin().toLowerCase()); - if (existingUser.isPresent() && (!existingUser.get().getId().equals(userDTO.getId()))) { + if (existingUser.isPresent() && (!existingUser.orElseThrow().getId().equals(userDTO.getId()))) { throw new LoginAlreadyUsedException(); } Optional updatedUser = userService.updateUser(userDTO); @@ -164,7 +165,7 @@ public ResponseEntity updateUser(@Valid @RequestBody AdminUserDTO */ @GetMapping("/users") @PreAuthorize("hasAuthority(\"" + AuthoritiesConstants.ADMIN + "\")") - public ResponseEntity> getAllUsers(@org.springdoc.api.annotations.ParameterObject Pageable pageable) { + public ResponseEntity> getAllUsers(@org.springdoc.core.annotations.ParameterObject Pageable pageable) { log.debug("REST request to get all User for an admin"); if (!onlyContainsAllowedProperties(pageable)) { return ResponseEntity.badRequest().build(); @@ -187,7 +188,7 @@ private boolean onlyContainsAllowedProperties(Pageable pageable) { */ @GetMapping("/users/{login}") @PreAuthorize("hasAuthority(\"" + AuthoritiesConstants.ADMIN + "\")") - public ResponseEntity getUser(@PathVariable @Pattern(regexp = Constants.LOGIN_REGEX) String login) { + public ResponseEntity getUser(@PathVariable("login") @Pattern(regexp = Constants.LOGIN_REGEX) String login) { log.debug("REST request to get User : {}", login); return ResponseUtil.wrapOrNotFound(userService.getUserWithAuthoritiesByLogin(login).map(AdminUserDTO::new)); } @@ -200,7 +201,7 @@ public ResponseEntity getUser(@PathVariable @Pattern(regexp = Cons */ @DeleteMapping("/users/{login}") @PreAuthorize("hasAuthority(\"" + AuthoritiesConstants.ADMIN + "\")") - public ResponseEntity deleteUser(@PathVariable @Pattern(regexp = Constants.LOGIN_REGEX) String login) { + public ResponseEntity deleteUser(@PathVariable("login") @Pattern(regexp = Constants.LOGIN_REGEX) String login) { log.debug("REST request to delete User: {}", login); userService.deleteUser(login); return ResponseEntity.noContent().headers(HeaderUtil.createAlert(applicationName, "userManagement.deleted", login)).build(); diff --git a/src/main/java/org/jhipster/health/web/rest/WeightResource.java b/src/main/java/org/jhipster/health/web/rest/WeightResource.java index 6e755140..37339354 100644 --- a/src/main/java/org/jhipster/health/web/rest/WeightResource.java +++ b/src/main/java/org/jhipster/health/web/rest/WeightResource.java @@ -1,18 +1,17 @@ package org.jhipster.health.web.rest; -import static org.elasticsearch.index.query.QueryBuilders.*; - +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; import java.net.URI; import java.net.URISyntaxException; import java.util.List; import java.util.Objects; import java.util.Optional; -import javax.validation.Valid; -import javax.validation.constraints.NotNull; import org.jhipster.health.domain.Weight; import org.jhipster.health.repository.WeightRepository; import org.jhipster.health.repository.search.WeightSearchRepository; import org.jhipster.health.web.rest.errors.BadRequestAlertException; +import org.jhipster.health.web.rest.errors.ElasticsearchExceptionMapper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; @@ -31,7 +30,7 @@ * REST controller for managing {@link org.jhipster.health.domain.Weight}. */ @RestController -@RequestMapping("/api") +@RequestMapping("/api/weights") @Transactional public class WeightResource { @@ -58,17 +57,17 @@ public WeightResource(WeightRepository weightRepository, WeightSearchRepository * @return the {@link ResponseEntity} with status {@code 201 (Created)} and with body the new weight, or with status {@code 400 (Bad Request)} if the weight has already an ID. * @throws URISyntaxException if the Location URI syntax is incorrect. */ - @PostMapping("/weights") + @PostMapping("") public ResponseEntity createWeight(@Valid @RequestBody Weight weight) throws URISyntaxException { log.debug("REST request to save Weight : {}", weight); if (weight.getId() != null) { throw new BadRequestAlertException("A new weight cannot already have an ID", ENTITY_NAME, "idexists"); } - Weight result = weightRepository.save(weight); - weightSearchRepository.index(result); - return ResponseEntity.created(new URI("/api/weights/" + result.getId())) - .headers(HeaderUtil.createEntityCreationAlert(applicationName, true, ENTITY_NAME, result.getId().toString())) - .body(result); + weight = weightRepository.save(weight); + weightSearchRepository.index(weight); + return ResponseEntity.created(new URI("/api/weights/" + weight.getId())) + .headers(HeaderUtil.createEntityCreationAlert(applicationName, true, ENTITY_NAME, weight.getId().toString())) + .body(weight); } /** @@ -81,7 +80,7 @@ public ResponseEntity createWeight(@Valid @RequestBody Weight weight) th * or with status {@code 500 (Internal Server Error)} if the weight couldn't be updated. * @throws URISyntaxException if the Location URI syntax is incorrect. */ - @PutMapping("/weights/{id}") + @PutMapping("/{id}") public ResponseEntity updateWeight( @PathVariable(value = "id", required = false) final Long id, @Valid @RequestBody Weight weight @@ -98,11 +97,11 @@ public ResponseEntity updateWeight( throw new BadRequestAlertException("Entity not found", ENTITY_NAME, "idnotfound"); } - Weight result = weightRepository.save(weight); - weightSearchRepository.index(result); + weight = weightRepository.save(weight); + weightSearchRepository.index(weight); return ResponseEntity.ok() .headers(HeaderUtil.createEntityUpdateAlert(applicationName, true, ENTITY_NAME, weight.getId().toString())) - .body(result); + .body(weight); } /** @@ -116,7 +115,7 @@ public ResponseEntity updateWeight( * or with status {@code 500 (Internal Server Error)} if the weight couldn't be updated. * @throws URISyntaxException if the Location URI syntax is incorrect. */ - @PatchMapping(value = "/weights/{id}", consumes = { "application/json", "application/merge-patch+json" }) + @PatchMapping(value = "/{id}", consumes = { "application/json", "application/merge-patch+json" }) public ResponseEntity partialUpdateWeight( @PathVariable(value = "id", required = false) final Long id, @NotNull @RequestBody Weight weight @@ -147,8 +146,7 @@ public ResponseEntity partialUpdateWeight( }) .map(weightRepository::save) .map(savedWeight -> { - weightSearchRepository.save(savedWeight); - + weightSearchRepository.index(savedWeight); return savedWeight; }); @@ -165,10 +163,10 @@ public ResponseEntity partialUpdateWeight( * @param eagerload flag to eager load entities from relationships (This is applicable for many-to-many). * @return the {@link ResponseEntity} with status {@code 200 (OK)} and the list of weights in body. */ - @GetMapping("/weights") + @GetMapping("") public ResponseEntity> getAllWeights( - @org.springdoc.api.annotations.ParameterObject Pageable pageable, - @RequestParam(required = false, defaultValue = "false") boolean eagerload + @org.springdoc.core.annotations.ParameterObject Pageable pageable, + @RequestParam(name = "eagerload", required = false, defaultValue = "true") boolean eagerload ) { log.debug("REST request to get a page of Weights"); Page page; @@ -187,8 +185,8 @@ public ResponseEntity> getAllWeights( * @param id the id of the weight to retrieve. * @return the {@link ResponseEntity} with status {@code 200 (OK)} and with body the weight, or with status {@code 404 (Not Found)}. */ - @GetMapping("/weights/{id}") - public ResponseEntity getWeight(@PathVariable Long id) { + @GetMapping("/{id}") + public ResponseEntity getWeight(@PathVariable("id") Long id) { log.debug("REST request to get Weight : {}", id); Optional weight = weightRepository.findOneWithEagerRelationships(id); return ResponseUtil.wrapOrNotFound(weight); @@ -200,32 +198,36 @@ public ResponseEntity getWeight(@PathVariable Long id) { * @param id the id of the weight to delete. * @return the {@link ResponseEntity} with status {@code 204 (NO_CONTENT)}. */ - @DeleteMapping("/weights/{id}") - public ResponseEntity deleteWeight(@PathVariable Long id) { + @DeleteMapping("/{id}") + public ResponseEntity deleteWeight(@PathVariable("id") Long id) { log.debug("REST request to delete Weight : {}", id); weightRepository.deleteById(id); - weightSearchRepository.deleteById(id); + weightSearchRepository.deleteFromIndexById(id); return ResponseEntity.noContent() .headers(HeaderUtil.createEntityDeletionAlert(applicationName, true, ENTITY_NAME, id.toString())) .build(); } /** - * {@code SEARCH /_search/weights?query=:query} : search for the weight corresponding + * {@code SEARCH /weights/_search?query=:query} : search for the weight corresponding * to the query. * * @param query the query of the weight search. * @param pageable the pagination information. * @return the result of the search. */ - @GetMapping("/_search/weights") + @GetMapping("/_search") public ResponseEntity> searchWeights( - @RequestParam String query, - @org.springdoc.api.annotations.ParameterObject Pageable pageable + @RequestParam("query") String query, + @org.springdoc.core.annotations.ParameterObject Pageable pageable ) { log.debug("REST request to search for a page of Weights for query {}", query); - Page page = weightSearchRepository.search(query, pageable); - HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(ServletUriComponentsBuilder.fromCurrentRequest(), page); - return ResponseEntity.ok().headers(headers).body(page.getContent()); + try { + Page page = weightSearchRepository.search(query, pageable); + HttpHeaders headers = PaginationUtil.generatePaginationHttpHeaders(ServletUriComponentsBuilder.fromCurrentRequest(), page); + return ResponseEntity.ok().headers(headers).body(page.getContent()); + } catch (RuntimeException e) { + throw ElasticsearchExceptionMapper.mapException(e); + } } } diff --git a/src/main/java/org/jhipster/health/web/rest/errors/BadRequestAlertException.java b/src/main/java/org/jhipster/health/web/rest/errors/BadRequestAlertException.java index 4f5018e1..119d27af 100644 --- a/src/main/java/org/jhipster/health/web/rest/errors/BadRequestAlertException.java +++ b/src/main/java/org/jhipster/health/web/rest/errors/BadRequestAlertException.java @@ -1,13 +1,13 @@ package org.jhipster.health.web.rest.errors; import java.net.URI; -import java.util.HashMap; -import java.util.Map; -import org.zalando.problem.AbstractThrowableProblem; -import org.zalando.problem.Status; +import org.springframework.http.HttpStatus; +import org.springframework.web.ErrorResponseException; +import tech.jhipster.web.rest.errors.ProblemDetailWithCause; +import tech.jhipster.web.rest.errors.ProblemDetailWithCause.ProblemDetailWithCauseBuilder; @SuppressWarnings("java:S110") // Inheritance tree of classes should not be too deep -public class BadRequestAlertException extends AbstractThrowableProblem { +public class BadRequestAlertException extends ErrorResponseException { private static final long serialVersionUID = 1L; @@ -20,7 +20,17 @@ public BadRequestAlertException(String defaultMessage, String entityName, String } public BadRequestAlertException(URI type, String defaultMessage, String entityName, String errorKey) { - super(type, defaultMessage, Status.BAD_REQUEST, null, null, null, getAlertParameters(entityName, errorKey)); + super( + HttpStatus.BAD_REQUEST, + ProblemDetailWithCauseBuilder.instance() + .withStatus(HttpStatus.BAD_REQUEST.value()) + .withType(type) + .withTitle(defaultMessage) + .withProperty("message", "error." + errorKey) + .withProperty("params", entityName) + .build(), + null + ); this.entityName = entityName; this.errorKey = errorKey; } @@ -33,10 +43,7 @@ public String getErrorKey() { return errorKey; } - private static Map getAlertParameters(String entityName, String errorKey) { - Map parameters = new HashMap<>(); - parameters.put("message", "error." + errorKey); - parameters.put("params", entityName); - return parameters; + public ProblemDetailWithCause getProblemDetailWithCause() { + return (ProblemDetailWithCause) this.getBody(); } } diff --git a/src/main/java/org/jhipster/health/web/rest/errors/ElasticsearchExceptionMapper.java b/src/main/java/org/jhipster/health/web/rest/errors/ElasticsearchExceptionMapper.java new file mode 100644 index 00000000..2f29221c --- /dev/null +++ b/src/main/java/org/jhipster/health/web/rest/errors/ElasticsearchExceptionMapper.java @@ -0,0 +1,30 @@ +package org.jhipster.health.web.rest.errors; + +import co.elastic.clients.elasticsearch._types.ElasticsearchException; +import co.elastic.clients.elasticsearch._types.ErrorCause; +import java.util.List; +import org.springframework.data.elasticsearch.UncategorizedElasticsearchException; + +public class ElasticsearchExceptionMapper { + + private ElasticsearchExceptionMapper() {} + + public static RuntimeException mapException(RuntimeException originalException) { + RuntimeException e = originalException; + if (e.getCause() instanceof UncategorizedElasticsearchException) { + e = (UncategorizedElasticsearchException) e.getCause(); + } + if (e.getCause() instanceof ElasticsearchException) { + ElasticsearchException esException = (ElasticsearchException) e.getCause(); + List rootCause = esException.response().error().rootCause(); + if (!rootCause.isEmpty()) { + String reason = rootCause.get(0).reason(); + if (reason != null && reason.startsWith("Failed to parse query [")) { + return new QuerySyntaxException(); + } + } + } + + return originalException; + } +} diff --git a/src/main/java/org/jhipster/health/web/rest/errors/ExceptionTranslator.java b/src/main/java/org/jhipster/health/web/rest/errors/ExceptionTranslator.java index 2d155f98..3398ec81 100644 --- a/src/main/java/org/jhipster/health/web/rest/errors/ExceptionTranslator.java +++ b/src/main/java/org/jhipster/health/web/rest/errors/ExceptionTranslator.java @@ -1,35 +1,39 @@ package org.jhipster.health.web.rest.errors; +import static org.springframework.core.annotation.AnnotatedElementUtils.findMergedAnnotation; + +import jakarta.servlet.http.HttpServletRequest; import java.net.URI; import java.util.Arrays; import java.util.Collection; import java.util.List; +import java.util.Map; import java.util.Optional; -import java.util.stream.Collectors; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import javax.servlet.http.HttpServletRequest; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.core.env.Environment; import org.springframework.dao.ConcurrencyFailureException; import org.springframework.dao.DataAccessException; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.HttpStatusCode; import org.springframework.http.ResponseEntity; import org.springframework.http.converter.HttpMessageConversionException; -import org.springframework.validation.BindingResult; +import org.springframework.lang.Nullable; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.authentication.BadCredentialsException; +import org.springframework.web.ErrorResponse; +import org.springframework.web.ErrorResponseException; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.context.request.NativeWebRequest; -import org.zalando.problem.DefaultProblem; -import org.zalando.problem.Problem; -import org.zalando.problem.ProblemBuilder; -import org.zalando.problem.Status; -import org.zalando.problem.StatusType; -import org.zalando.problem.spring.web.advice.ProblemHandling; -import org.zalando.problem.spring.web.advice.security.SecurityAdviceTrait; -import org.zalando.problem.violations.ConstraintViolationProblem; +import org.springframework.web.context.request.WebRequest; +import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; import tech.jhipster.config.JHipsterConstants; +import tech.jhipster.web.rest.errors.ProblemDetailWithCause; +import tech.jhipster.web.rest.errors.ProblemDetailWithCause.ProblemDetailWithCauseBuilder; import tech.jhipster.web.util.HeaderUtil; /** @@ -37,12 +41,12 @@ * The error response follows RFC7807 - Problem Details for HTTP APIs (https://tools.ietf.org/html/rfc7807). */ @ControllerAdvice -public class ExceptionTranslator implements ProblemHandling, SecurityAdviceTrait { +public class ExceptionTranslator extends ResponseEntityExceptionHandler { private static final String FIELD_ERRORS_KEY = "fieldErrors"; private static final String MESSAGE_KEY = "message"; private static final String PATH_KEY = "path"; - private static final String VIOLATIONS_KEY = "violations"; + private static final boolean CASUAL_CHAIN_ENABLED = false; @Value("${jhipster.clientApp.name}") private String applicationName; @@ -53,45 +57,88 @@ public ExceptionTranslator(Environment env) { this.env = env; } - /** - * Post-process the Problem payload to add the message key for the front-end if needed. - */ + @ExceptionHandler + public ResponseEntity handleAnyException(Throwable ex, NativeWebRequest request) { + ProblemDetailWithCause pdCause = wrapAndCustomizeProblem(ex, request); + return handleExceptionInternal((Exception) ex, pdCause, buildHeaders(ex), HttpStatusCode.valueOf(pdCause.getStatus()), request); + } + + @Nullable @Override - public ResponseEntity process(@Nullable ResponseEntity entity, NativeWebRequest request) { - if (entity == null) { - return null; - } - Problem problem = entity.getBody(); - if (!(problem instanceof ConstraintViolationProblem || problem instanceof DefaultProblem)) { - return entity; + protected ResponseEntity handleExceptionInternal( + Exception ex, + @Nullable Object body, + HttpHeaders headers, + HttpStatusCode statusCode, + WebRequest request + ) { + body = body == null ? wrapAndCustomizeProblem((Throwable) ex, (NativeWebRequest) request) : body; + return super.handleExceptionInternal(ex, body, headers, statusCode, request); + } + + protected ProblemDetailWithCause wrapAndCustomizeProblem(Throwable ex, NativeWebRequest request) { + return customizeProblem(getProblemDetailWithCause(ex), ex, request); + } + + private ProblemDetailWithCause getProblemDetailWithCause(Throwable ex) { + if ( + ex instanceof org.jhipster.health.service.UsernameAlreadyUsedException + ) return (ProblemDetailWithCause) new LoginAlreadyUsedException().getBody(); + if ( + ex instanceof org.jhipster.health.service.EmailAlreadyUsedException + ) return (ProblemDetailWithCause) new EmailAlreadyUsedException().getBody(); + if ( + ex instanceof org.jhipster.health.service.InvalidPasswordException + ) return (ProblemDetailWithCause) new InvalidPasswordException().getBody(); + + if ( + ex instanceof ErrorResponseException exp && exp.getBody() instanceof ProblemDetailWithCause problemDetailWithCause + ) return problemDetailWithCause; + return ProblemDetailWithCauseBuilder.instance().withStatus(toStatus(ex).value()).build(); + } + + protected ProblemDetailWithCause customizeProblem(ProblemDetailWithCause problem, Throwable err, NativeWebRequest request) { + if (problem.getStatus() <= 0) problem.setStatus(toStatus(err)); + + if (problem.getType() == null || problem.getType().equals(URI.create("about:blank"))) problem.setType(getMappedType(err)); + + // higher precedence to Custom/ResponseStatus types + String title = extractTitle(err, problem.getStatus()); + String problemTitle = problem.getTitle(); + if (problemTitle == null || !problemTitle.equals(title)) { + problem.setTitle(title); } - HttpServletRequest nativeRequest = request.getNativeRequest(HttpServletRequest.class); - String requestUri = nativeRequest != null ? nativeRequest.getRequestURI() : StringUtils.EMPTY; - ProblemBuilder builder = Problem.builder() - .withType(Problem.DEFAULT_TYPE.equals(problem.getType()) ? ErrorConstants.DEFAULT_TYPE : problem.getType()) - .withStatus(problem.getStatus()) - .withTitle(problem.getTitle()) - .with(PATH_KEY, requestUri); - - if (problem instanceof ConstraintViolationProblem) { - builder - .with(VIOLATIONS_KEY, ((ConstraintViolationProblem) problem).getViolations()) - .with(MESSAGE_KEY, ErrorConstants.ERR_VALIDATION); - } else { - builder.withCause(((DefaultProblem) problem).getCause()).withDetail(problem.getDetail()).withInstance(problem.getInstance()); - problem.getParameters().forEach(builder::with); - if (!problem.getParameters().containsKey(MESSAGE_KEY) && problem.getStatus() != null) { - builder.with(MESSAGE_KEY, "error.http." + problem.getStatus().getStatusCode()); - } + if (problem.getDetail() == null) { + // higher precedence to cause + problem.setDetail(getCustomizedErrorDetails(err)); } - return new ResponseEntity<>(builder.build(), entity.getHeaders(), entity.getStatusCode()); + + Map problemProperties = problem.getProperties(); + if (problemProperties == null || !problemProperties.containsKey(MESSAGE_KEY)) problem.setProperty( + MESSAGE_KEY, + getMappedMessageKey(err) != null ? getMappedMessageKey(err) : "error.http." + problem.getStatus() + ); + + if (problemProperties == null || !problemProperties.containsKey(PATH_KEY)) problem.setProperty(PATH_KEY, getPathValue(request)); + + if ( + (err instanceof MethodArgumentNotValidException fieldException) && + (problemProperties == null || !problemProperties.containsKey(FIELD_ERRORS_KEY)) + ) problem.setProperty(FIELD_ERRORS_KEY, getFieldErrors(fieldException)); + + problem.setCause(buildCause(err.getCause(), request).orElse(null)); + + return problem; } - @Override - public ResponseEntity handleMethodArgumentNotValid(MethodArgumentNotValidException ex, @Nonnull NativeWebRequest request) { - BindingResult result = ex.getBindingResult(); - List fieldErrors = result + private String extractTitle(Throwable err, int statusCode) { + return getCustomizedTitle(err) != null ? getCustomizedTitle(err) : extractTitleForResponseStatus(err, statusCode); + } + + private List getFieldErrors(MethodArgumentNotValidException ex) { + return ex + .getBindingResult() .getFieldErrors() .stream() .map( @@ -102,116 +149,105 @@ public ResponseEntity handleMethodArgumentNotValid(MethodArgumentNotVal StringUtils.isNotBlank(f.getDefaultMessage()) ? f.getDefaultMessage() : f.getCode() ) ) - .collect(Collectors.toList()); + .toList(); + } - Problem problem = Problem.builder() - .withType(ErrorConstants.CONSTRAINT_VIOLATION_TYPE) - .withTitle("Method argument not valid") - .withStatus(defaultConstraintViolationStatus()) - .with(MESSAGE_KEY, ErrorConstants.ERR_VALIDATION) - .with(FIELD_ERRORS_KEY, fieldErrors) - .build(); - return create(ex, problem, request); + private String extractTitleForResponseStatus(Throwable err, int statusCode) { + ResponseStatus specialStatus = extractResponseStatus(err); + return specialStatus == null ? HttpStatus.valueOf(statusCode).getReasonPhrase() : specialStatus.reason(); } - @ExceptionHandler - public ResponseEntity handleEmailAlreadyUsedException( - org.jhipster.health.service.EmailAlreadyUsedException ex, - NativeWebRequest request - ) { - EmailAlreadyUsedException problem = new EmailAlreadyUsedException(); - return create( - problem, - request, - HeaderUtil.createFailureAlert(applicationName, true, problem.getEntityName(), problem.getErrorKey(), problem.getMessage()) - ); + private String extractURI(NativeWebRequest request) { + HttpServletRequest nativeRequest = request.getNativeRequest(HttpServletRequest.class); + return nativeRequest != null ? nativeRequest.getRequestURI() : StringUtils.EMPTY; } - @ExceptionHandler - public ResponseEntity handleUsernameAlreadyUsedException( - org.jhipster.health.service.UsernameAlreadyUsedException ex, - NativeWebRequest request - ) { - LoginAlreadyUsedException problem = new LoginAlreadyUsedException(); - return create( - problem, - request, - HeaderUtil.createFailureAlert(applicationName, true, problem.getEntityName(), problem.getErrorKey(), problem.getMessage()) + private HttpStatus toStatus(final Throwable throwable) { + // Let the ErrorResponse take this responsibility + if (throwable instanceof ErrorResponse err) return HttpStatus.valueOf(err.getBody().getStatus()); + + return Optional.ofNullable(getMappedStatus(throwable)).orElse( + Optional.ofNullable(resolveResponseStatus(throwable)).map(ResponseStatus::value).orElse(HttpStatus.INTERNAL_SERVER_ERROR) ); } - @ExceptionHandler - public ResponseEntity handleInvalidPasswordException( - org.jhipster.health.service.InvalidPasswordException ex, - NativeWebRequest request - ) { - return create(new InvalidPasswordException(), request); + private ResponseStatus extractResponseStatus(final Throwable throwable) { + return Optional.ofNullable(resolveResponseStatus(throwable)).orElse(null); } - @ExceptionHandler - public ResponseEntity handleBadRequestAlertException(BadRequestAlertException ex, NativeWebRequest request) { - return create( - ex, - request, - HeaderUtil.createFailureAlert(applicationName, true, ex.getEntityName(), ex.getErrorKey(), ex.getMessage()) - ); + private ResponseStatus resolveResponseStatus(final Throwable type) { + final ResponseStatus candidate = findMergedAnnotation(type.getClass(), ResponseStatus.class); + return candidate == null && type.getCause() != null ? resolveResponseStatus(type.getCause()) : candidate; } - @ExceptionHandler - public ResponseEntity handleConcurrencyFailure(ConcurrencyFailureException ex, NativeWebRequest request) { - Problem problem = Problem.builder().withStatus(Status.CONFLICT).with(MESSAGE_KEY, ErrorConstants.ERR_CONCURRENCY_FAILURE).build(); - return create(ex, problem, request); + private URI getMappedType(Throwable err) { + if (err instanceof MethodArgumentNotValidException) return ErrorConstants.CONSTRAINT_VIOLATION_TYPE; + return ErrorConstants.DEFAULT_TYPE; } - @Override - public ProblemBuilder prepare(final Throwable throwable, final StatusType status, final URI type) { - Collection activeProfiles = Arrays.asList(env.getActiveProfiles()); + private String getMappedMessageKey(Throwable err) { + if (err instanceof MethodArgumentNotValidException) { + return ErrorConstants.ERR_VALIDATION; + } else if (err instanceof ConcurrencyFailureException || err.getCause() instanceof ConcurrencyFailureException) { + return ErrorConstants.ERR_CONCURRENCY_FAILURE; + } + return null; + } + private String getCustomizedTitle(Throwable err) { + if (err instanceof MethodArgumentNotValidException) return "Method argument not valid"; + return null; + } + + private String getCustomizedErrorDetails(Throwable err) { + Collection activeProfiles = Arrays.asList(env.getActiveProfiles()); if (activeProfiles.contains(JHipsterConstants.SPRING_PROFILE_PRODUCTION)) { - if (throwable instanceof HttpMessageConversionException) { - return Problem.builder() - .withType(type) - .withTitle(status.getReasonPhrase()) - .withStatus(status) - .withDetail("Unable to convert http message") - .withCause( - Optional.ofNullable(throwable.getCause()).filter(cause -> isCausalChainsEnabled()).map(this::toProblem).orElse(null) - ); - } - if (throwable instanceof DataAccessException) { - return Problem.builder() - .withType(type) - .withTitle(status.getReasonPhrase()) - .withStatus(status) - .withDetail("Failure during data access") - .withCause( - Optional.ofNullable(throwable.getCause()).filter(cause -> isCausalChainsEnabled()).map(this::toProblem).orElse(null) - ); - } - if (containsPackageName(throwable.getMessage())) { - return Problem.builder() - .withType(type) - .withTitle(status.getReasonPhrase()) - .withStatus(status) - .withDetail("Unexpected runtime exception") - .withCause( - Optional.ofNullable(throwable.getCause()).filter(cause -> isCausalChainsEnabled()).map(this::toProblem).orElse(null) - ); - } + if (err instanceof HttpMessageConversionException) return "Unable to convert http message"; + if (err instanceof DataAccessException) return "Failure during data access"; + if (containsPackageName(err.getMessage())) return "Unexpected runtime exception"; } + return err.getCause() != null ? err.getCause().getMessage() : err.getMessage(); + } + + private HttpStatus getMappedStatus(Throwable err) { + // Where we disagree with Spring defaults + if (err instanceof AccessDeniedException) return HttpStatus.FORBIDDEN; + if (err instanceof ConcurrencyFailureException) return HttpStatus.CONFLICT; + if (err instanceof BadCredentialsException) return HttpStatus.UNAUTHORIZED; + return null; + } + + private URI getPathValue(NativeWebRequest request) { + if (request == null) return URI.create("about:blank"); + return URI.create(extractURI(request)); + } + + private HttpHeaders buildHeaders(Throwable err) { + return err instanceof BadRequestAlertException badRequestAlertException + ? HeaderUtil.createFailureAlert( + applicationName, + true, + badRequestAlertException.getEntityName(), + badRequestAlertException.getErrorKey(), + badRequestAlertException.getMessage() + ) + : null; + } + + public Optional buildCause(final Throwable throwable, NativeWebRequest request) { + if (throwable != null && isCasualChainEnabled()) { + return Optional.of(customizeProblem(getProblemDetailWithCause(throwable), throwable, request)); + } + return Optional.ofNullable(null); + } - return Problem.builder() - .withType(type) - .withTitle(status.getReasonPhrase()) - .withStatus(status) - .withDetail(throwable.getMessage()) - .withCause( - Optional.ofNullable(throwable.getCause()).filter(cause -> isCausalChainsEnabled()).map(this::toProblem).orElse(null) - ); + private boolean isCasualChainEnabled() { + // Customize as per the needs + return CASUAL_CHAIN_ENABLED; } private boolean containsPackageName(String message) { // This list is for sure not complete - return StringUtils.containsAny(message, "org.", "java.", "net.", "javax.", "com.", "io.", "de.", "org.jhipster.health"); + return StringUtils.containsAny(message, "org.", "java.", "net.", "jakarta.", "javax.", "com.", "io.", "de.", "org.jhipster.health"); } } diff --git a/src/main/java/org/jhipster/health/web/rest/errors/InvalidPasswordException.java b/src/main/java/org/jhipster/health/web/rest/errors/InvalidPasswordException.java index cdad1c4d..a2664fdb 100644 --- a/src/main/java/org/jhipster/health/web/rest/errors/InvalidPasswordException.java +++ b/src/main/java/org/jhipster/health/web/rest/errors/InvalidPasswordException.java @@ -1,14 +1,23 @@ package org.jhipster.health.web.rest.errors; -import org.zalando.problem.AbstractThrowableProblem; -import org.zalando.problem.Status; +import org.springframework.http.HttpStatus; +import org.springframework.web.ErrorResponseException; +import tech.jhipster.web.rest.errors.ProblemDetailWithCause.ProblemDetailWithCauseBuilder; @SuppressWarnings("java:S110") // Inheritance tree of classes should not be too deep -public class InvalidPasswordException extends AbstractThrowableProblem { +public class InvalidPasswordException extends ErrorResponseException { private static final long serialVersionUID = 1L; public InvalidPasswordException() { - super(ErrorConstants.INVALID_PASSWORD_TYPE, "Incorrect password", Status.BAD_REQUEST); + super( + HttpStatus.BAD_REQUEST, + ProblemDetailWithCauseBuilder.instance() + .withStatus(HttpStatus.BAD_REQUEST.value()) + .withType(ErrorConstants.INVALID_PASSWORD_TYPE) + .withTitle("Incorrect password") + .build(), + null + ); } } diff --git a/src/main/java/org/jhipster/health/web/rest/errors/QuerySyntaxException.java b/src/main/java/org/jhipster/health/web/rest/errors/QuerySyntaxException.java new file mode 100644 index 00000000..64e6d991 --- /dev/null +++ b/src/main/java/org/jhipster/health/web/rest/errors/QuerySyntaxException.java @@ -0,0 +1,11 @@ +package org.jhipster.health.web.rest.errors; + +@SuppressWarnings("java:S110") // Inheritance tree of classes should not be too deep +public class QuerySyntaxException extends BadRequestAlertException { + + private static final long serialVersionUID = 1L; + + public QuerySyntaxException() { + super("Invalid query syntax!", "elasticseach", "querySyntaxError"); + } +} diff --git a/src/main/java/org/jhipster/health/web/rest/errors/package-info.java b/src/main/java/org/jhipster/health/web/rest/errors/package-info.java index 3624a508..e0d90bd7 100644 --- a/src/main/java/org/jhipster/health/web/rest/errors/package-info.java +++ b/src/main/java/org/jhipster/health/web/rest/errors/package-info.java @@ -1,6 +1,4 @@ /** - * Specific errors used with Zalando's "problem-spring-web" library. - * - * More information on https://github.com/zalando/problem-spring-web + * Rest layer error handling. */ package org.jhipster.health.web.rest.errors; diff --git a/src/main/java/org/jhipster/health/web/rest/package-info.java b/src/main/java/org/jhipster/health/web/rest/package-info.java index 7287b937..c611c415 100644 --- a/src/main/java/org/jhipster/health/web/rest/package-info.java +++ b/src/main/java/org/jhipster/health/web/rest/package-info.java @@ -1,4 +1,4 @@ /** - * Spring MVC REST controllers. + * Rest layer. */ package org.jhipster.health.web.rest; diff --git a/src/main/java/org/jhipster/health/web/rest/vm/LoginVM.java b/src/main/java/org/jhipster/health/web/rest/vm/LoginVM.java index a853c39a..b7ad4fb3 100644 --- a/src/main/java/org/jhipster/health/web/rest/vm/LoginVM.java +++ b/src/main/java/org/jhipster/health/web/rest/vm/LoginVM.java @@ -1,7 +1,7 @@ package org.jhipster.health.web.rest.vm; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Size; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Size; /** * View Model object for storing a user's credentials. diff --git a/src/main/java/org/jhipster/health/web/rest/vm/ManagedUserVM.java b/src/main/java/org/jhipster/health/web/rest/vm/ManagedUserVM.java index d3e058d5..30d03cf1 100644 --- a/src/main/java/org/jhipster/health/web/rest/vm/ManagedUserVM.java +++ b/src/main/java/org/jhipster/health/web/rest/vm/ManagedUserVM.java @@ -1,6 +1,6 @@ package org.jhipster.health.web.rest.vm; -import javax.validation.constraints.Size; +import jakarta.validation.constraints.Size; import org.jhipster.health.service.dto.AdminUserDTO; /** diff --git a/src/main/java/org/jhipster/health/web/rest/vm/package-info.java b/src/main/java/org/jhipster/health/web/rest/vm/package-info.java index 874b4f69..50facd1e 100644 --- a/src/main/java/org/jhipster/health/web/rest/vm/package-info.java +++ b/src/main/java/org/jhipster/health/web/rest/vm/package-info.java @@ -1,4 +1,4 @@ /** - * View Models used by Spring MVC REST controllers. + * Rest layer visual models. */ package org.jhipster.health.web.rest.vm; diff --git a/src/main/resources/banner.txt b/src/main/resources/banner.txt index e0bc55aa..5be7dbe6 100644 --- a/src/main/resources/banner.txt +++ b/src/main/resources/banner.txt @@ -6,5 +6,5 @@ ${AnsiColor.GREEN}╚██████╔╝${AnsiColor.RED} ██║ ██║ ████████╗ ██║ ██████╔╝ ██║ ████████╗ ██║ ╚██╗ ${AnsiColor.GREEN} ╚═════╝ ${AnsiColor.RED} ╚═╝ ╚═╝ ╚═══════╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══════╝ ╚═╝ ╚═╝ -${AnsiColor.BRIGHT_BLUE}:: JHipster 🤓 :: Running Spring Boot ${spring-boot.version} :: +${AnsiColor.BRIGHT_BLUE}:: JHipster 🤓 :: Running Spring Boot ${spring-boot.version} :: Startup profile(s) ${spring.profiles.active} :: :: https://www.jhipster.tech ::${AnsiColor.DEFAULT} diff --git a/src/main/resources/config/application-dev.yml b/src/main/resources/config/application-dev.yml index fe86d5e5..b84335a0 100644 --- a/src/main/resources/config/application-dev.yml +++ b/src/main/resources/config/application-dev.yml @@ -37,7 +37,7 @@ spring: indent-output: true datasource: type: com.zaxxer.hikari.HikariDataSource - url: jdbc:h2:file:./build/h2db/db/twentyonepoints;DB_CLOSE_DELAY=-1;MODE=LEGACY + url: jdbc:h2:file:./build/h2db/db/TwentyOnePoints;DB_CLOSE_DELAY=-1 username: TwentyOnePoints password: hikari: @@ -47,7 +47,6 @@ spring: console: # disable spring boot built-in h2-console since we start it manually with correct configuration enabled: false - jpa: elasticsearch: uris: http://localhost:9200 liquibase: @@ -104,7 +103,7 @@ jhipster: enabled: false host: localhost port: 5000 - queue-size: 512 + ring-buffer-size: 512 # =================================================================== # Application specific properties # Add your own application properties here, see the ApplicationProperties class diff --git a/src/main/resources/config/application-prod.yml b/src/main/resources/config/application-prod.yml index 5a4041a6..1587a587 100644 --- a/src/main/resources/config/application-prod.yml +++ b/src/main/resources/config/application-prod.yml @@ -20,9 +20,9 @@ logging: org.jhipster.health: INFO management: - metrics: - export: - prometheus: + prometheus: + metrics: + export: enabled: false spring: @@ -39,8 +39,6 @@ spring: hikari: poolName: Hikari auto-commit: false - jpa: - database-platform: tech.jhipster.domain.util.FixedPostgreSQL10Dialect elasticsearch: uris: http://localhost:9200 # Replace by 'prod, faker' to add the faker context and have sample data loaded in production @@ -100,7 +98,7 @@ jhipster: jwt: # This token must be encoded using Base64 and be at least 256 bits long (you can type `openssl rand -base64 64` on your command line to generate a 512 bits one) # As this is the PRODUCTION configuration, you MUST change the default key, and store it securely: - # - In the JHipster Registry (which includes a Spring Cloud Config server) + # - In the Consul configserver # - In a separate `application-prod.yml` file, in the same folder as your executable JAR file # - In the `JHIPSTER_SECURITY_AUTHENTICATION_JWT_BASE64_SECRET` environment variable base64-secret: YjAwZDU2ODI3NzNjZjk0NjVhY2UzNGViZmY0YjY1ZGY2MDI5MTc5Y2FlZWYxNzE5Yjg5ZjMxOGZhZTgwZjA5NTFkN2VmNDM3ZGMyZDNjZTViNDAwYTg4NmNlMmM2ZDkxZTMwMDk5YmUxNWNkZmE0YTNiNDlhMGNhZmM4OTk1NmQ= @@ -115,7 +113,7 @@ jhipster: enabled: false host: localhost port: 5000 - queue-size: 512 + ring-buffer-size: 512 # =================================================================== # Application specific properties # Add your own application properties here, see the ApplicationProperties class diff --git a/src/main/resources/config/application.yml b/src/main/resources/config/application.yml index b640d6cf..943701d7 100644 --- a/src/main/resources/config/application.yml +++ b/src/main/resources/config/application.yml @@ -30,20 +30,18 @@ management: base-path: /management exposure: include: - [ - 'configprops', - 'env', - 'health', - 'info', - 'jhimetrics', - 'jhiopenapigroups', - 'logfile', - 'loggers', - 'prometheus', - 'threaddump', - 'caches', - 'liquibase', - ] + - configprops + - env + - health + - info + - jhimetrics + - jhiopenapigroups + - logfile + - loggers + - prometheus + - threaddump + - caches + - liquibase endpoint: health: show-details: when_authorized @@ -65,12 +63,15 @@ management: health: mail: enabled: false # When using the MailService, configure an SMTP server and set this to true - metrics: - export: - # Prometheus is the default metrics backend - prometheus: + prometheus: + metrics: + export: enabled: true step: 60 + observations: + key-values: + application: ${spring.application.name} + metrics: enable: http: true jvm: true @@ -82,13 +83,10 @@ management: all: true percentiles: all: 0, 0.5, 0.75, 0.95, 0.99, 1.0 - tags: - application: ${spring.application.name} - web: - server: - request: - autotime: - enabled: true + data: + repository: + autotime: + enabled: true spring: application: @@ -97,7 +95,7 @@ spring: # The commented value for `active` can be replaced with valid Spring profiles to load. # Otherwise, it will be filled in by gradle when building the JAR file # Either way, it can be overridden by `--spring.profiles.active` value passed in the commandline or `-Dspring.profiles.active` set in `JAVA_OPTS` - active: #spring.profiles.active# + active: '@spring.profiles.active@' group: dev: - dev @@ -114,6 +112,8 @@ spring: open-in-view: false properties: hibernate.jdbc.time_zone: UTC + hibernate.timezone.default_storage: NORMALIZE + hibernate.type.preferred_instant_jdbc_type: TIMESTAMP hibernate.id.new_generator_mappings: true hibernate.connection.provider_disables_autocommit: true hibernate.cache.use_second_level_cache: true @@ -128,15 +128,15 @@ spring: hibernate: ddl-auto: none naming: - physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy + physical-strategy: org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy implicit-strategy: org.springframework.boot.orm.jpa.hibernate.SpringImplicitNamingStrategy messages: basename: i18n/messages main: allow-bean-definition-overriding: true mvc: - pathmatch: - matching-strategy: ant_path_matcher + problemdetails: + enabled: true task: execution: thread-name-prefix: twenty-one-points-task- @@ -188,8 +188,8 @@ jhipster: mail: from: TwentyOnePoints@localhost api-docs: - default-include-pattern: ${server.servlet.context-path:}/api/** - management-include-pattern: ${server.servlet.context-path:}/management/** + default-include-pattern: /api/** + management-include-pattern: /management/** title: Twenty One Points API description: Twenty One Points API documentation version: 0.0.1 @@ -211,36 +211,3 @@ jhipster: # =================================================================== # application: - -# =================================================================== -# Uncomment or add these properties to your current application properties to -# enable this if you'd like to reindex entities with Elasticsearch -# =================================================================== - -# application: -# elasticsearch: -# reindex-on-startup: true -# =================================================================== -# Uncomment or add these properties to your current application properties to -# enable this if you'd like to reindex entities with Elasticsearch -# =================================================================== - -# application: -# elasticsearch: -# reindex-on-startup: true -# =================================================================== -# Uncomment or add these properties to your current application properties to -# enable this if you'd like to reindex entities with Elasticsearch -# =================================================================== - -# application: -# elasticsearch: -# reindex-on-startup: true -# =================================================================== -# Uncomment or add these properties to your current application properties to -# enable this if you'd like to reindex entities with Elasticsearch -# =================================================================== - -# application: -# elasticsearch: -# reindex-on-startup: true diff --git a/src/main/resources/config/bootstrap-prod.yml b/src/main/resources/config/bootstrap-prod.yml deleted file mode 100644 index 1047df3f..00000000 --- a/src/main/resources/config/bootstrap-prod.yml +++ /dev/null @@ -1,6 +0,0 @@ -# =================================================================== -# Spring Cloud Config bootstrap configuration for the "prod" profile -# =================================================================== - -spring: - cloud: diff --git a/src/main/resources/config/bootstrap.yml b/src/main/resources/config/bootstrap.yml deleted file mode 100644 index c4b2b068..00000000 --- a/src/main/resources/config/bootstrap.yml +++ /dev/null @@ -1,14 +0,0 @@ -# =================================================================== -# Spring Cloud Config bootstrap configuration for the "dev" profile -# In prod profile, properties will be overwritten by the ones defined in bootstrap-prod.yml -# =================================================================== - -spring: - application: - name: TwentyOnePoints - profiles: - # The commented value for `active` can be replaced with valid Spring profiles to load. - # Otherwise, it will be filled in by gradle when building the JAR file - # Either way, it can be overridden by `--spring.profiles.active` value passed in the commandline or `-Dspring.profiles.active` set in `JAVA_OPTS` - active: #spring.profiles.active# - cloud: diff --git a/src/main/resources/config/liquibase/changelog/00000000000000_initial_schema.xml b/src/main/resources/config/liquibase/changelog/00000000000000_initial_schema.xml index eaac648e..5f42c305 100644 --- a/src/main/resources/config/liquibase/changelog/00000000000000_initial_schema.xml +++ b/src/main/resources/config/liquibase/changelog/00000000000000_initial_schema.xml @@ -78,6 +78,13 @@ + + + - - - - + diff --git a/src/main/resources/config/liquibase/changelog/20221108000520_added_entity_constraints_Points.xml b/src/main/resources/config/liquibase/changelog/20221108000520_added_entity_constraints_Points.xml index 5a10479a..52b4ac95 100644 --- a/src/main/resources/config/liquibase/changelog/20221108000520_added_entity_constraints_Points.xml +++ b/src/main/resources/config/liquibase/changelog/20221108000520_added_entity_constraints_Points.xml @@ -1,8 +1,10 @@ + xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd + http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd"> @@ -12,6 +14,7 @@ baseTableName="points" constraintName="fk_points__user_id" referencedColumnNames="id" - referencedTableName="jhi_user"/> + referencedTableName="jhi_user" + /> diff --git a/src/main/resources/config/liquibase/changelog/20221108001452_added_entity_constraints_Weight.xml b/src/main/resources/config/liquibase/changelog/20221108001452_added_entity_constraints_Weight.xml index 738b6683..d821c831 100644 --- a/src/main/resources/config/liquibase/changelog/20221108001452_added_entity_constraints_Weight.xml +++ b/src/main/resources/config/liquibase/changelog/20221108001452_added_entity_constraints_Weight.xml @@ -1,8 +1,10 @@ + xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd + http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd"> @@ -12,6 +14,7 @@ baseTableName="weight" constraintName="fk_weight__user_id" referencedColumnNames="id" - referencedTableName="jhi_user"/> + referencedTableName="jhi_user" + /> diff --git a/src/main/resources/config/liquibase/changelog/20221108001642_added_entity_constraints_BloodPressure.xml b/src/main/resources/config/liquibase/changelog/20221108001642_added_entity_constraints_BloodPressure.xml index 042be63d..aa2e9e4d 100644 --- a/src/main/resources/config/liquibase/changelog/20221108001642_added_entity_constraints_BloodPressure.xml +++ b/src/main/resources/config/liquibase/changelog/20221108001642_added_entity_constraints_BloodPressure.xml @@ -1,8 +1,10 @@ + xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd + http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd"> @@ -12,6 +14,7 @@ baseTableName="blood_pressure" constraintName="fk_blood_pressure__user_id" referencedColumnNames="id" - referencedTableName="jhi_user"/> + referencedTableName="jhi_user" + /> diff --git a/src/main/resources/config/liquibase/changelog/20221108002021_added_entity_constraints_Preferences.xml b/src/main/resources/config/liquibase/changelog/20221108002021_added_entity_constraints_Preferences.xml index 39f1b940..8ee7ed45 100644 --- a/src/main/resources/config/liquibase/changelog/20221108002021_added_entity_constraints_Preferences.xml +++ b/src/main/resources/config/liquibase/changelog/20221108002021_added_entity_constraints_Preferences.xml @@ -1,8 +1,10 @@ + xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-latest.xsd + http://www.liquibase.org/xml/ns/dbchangelog-ext http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-ext.xsd"> @@ -12,6 +14,7 @@ baseTableName="preferences" constraintName="fk_preferences__user_id" referencedColumnNames="id" - referencedTableName="jhi_user"/> + referencedTableName="jhi_user" + /> diff --git a/src/main/resources/config/liquibase/fake-data/blood_pressure.csv b/src/main/resources/config/liquibase/fake-data/blood_pressure.csv index 1761aece..51103b97 100644 --- a/src/main/resources/config/liquibase/fake-data/blood_pressure.csv +++ b/src/main/resources/config/liquibase/fake-data/blood_pressure.csv @@ -1,11 +1,11 @@ id;timestamp;systolic;diastolic -1;2022-11-07T07:09:37;68340;14535 -2;2022-11-07T14:53:32;94757;59470 -3;2022-11-07T04:33:03;29537;85270 -4;2022-11-07T22:37:12;57203;24575 -5;2022-11-07T23:28:59;90887;37312 -6;2022-11-07T08:13:54;79996;12371 -7;2022-11-07T08:34:39;65537;16335 -8;2022-11-07T14:39:25;64136;3779 -9;2022-11-07T14:10:33;63360;82042 -10;2022-11-07T09:10:06;35856;39254 +1;2022-11-07T14:43:04;12478;13821 +2;2022-11-07T12:57:34;13165;18583 +3;2022-11-07T01:39:20;15280;8098 +4;2022-11-07T14:45:57;9175;1981 +5;2022-11-07T14:43:39;24065;8112 +6;2022-11-07T08:33:37;21430;22236 +7;2022-11-07T19:34:10;26264;24672 +8;2022-11-07T23:12:13;25591;30267 +9;2022-11-07T11:48:21;3023;16947 +10;2022-11-07T05:28:03;15142;9553 diff --git a/src/main/resources/config/liquibase/fake-data/points.csv b/src/main/resources/config/liquibase/fake-data/points.csv index 4c666786..82fe80c4 100644 --- a/src/main/resources/config/liquibase/fake-data/points.csv +++ b/src/main/resources/config/liquibase/fake-data/points.csv @@ -1,11 +1,11 @@ id;date;exercise;meals;alcohol;notes -1;2022-11-07;6198;38418;57707;withdrawal Bedfordshire Analyst -2;2022-11-07;2388;1106;9423;Account Garden New -3;2022-11-07;76967;8783;81008;e-business override bandwidth -4;2022-11-07;14053;35227;55062;dynamic Concrete -5;2022-11-07;3838;53842;86262;Drive Research -6;2022-11-07;88201;96453;19830;XSS -7;2022-11-07;45449;51221;53191;interfaces Frozen -8;2022-11-07;98590;58178;44831;program payment -9;2022-11-07;45405;85555;60037;transparent green robust -10;2022-11-07;9401;94414;46576;seize synthesize +1;2022-11-07;5604;15387;27389;substitute +2;2022-11-07;20124;21230;2524;stun orderly unto +3;2022-11-07;7629;30218;20026;randomize +4;2022-11-07;30715;18882;28255;drat +5;2022-11-07;5657;5102;21820;including than +6;2022-11-07;18986;42;25551;fooey including +7;2022-11-07;2858;32281;32639;whether tenderly +8;2022-11-07;28399;23624;25661;thankful +9;2022-11-07;18885;24916;26718;disconnect upright phone +10;2022-11-07;17461;28753;10264;until astonishing smear diff --git a/src/main/resources/config/liquibase/fake-data/preferences.csv b/src/main/resources/config/liquibase/fake-data/preferences.csv index e5e4d24d..195c2326 100644 --- a/src/main/resources/config/liquibase/fake-data/preferences.csv +++ b/src/main/resources/config/liquibase/fake-data/preferences.csv @@ -1,11 +1,11 @@ id;weekly_goal;weight_units -1;19;LB -2;15;LB -3;19;LB -4;21;KG -5;14;KG -6;11;KG -7;21;LB -8;20;KG -9;15;KG -10;15;LB +1;20;KG +2;18;KG +3;20;LB +4;10;LB +5;16;KG +6;10;LB +7;20;LB +8;15;LB +9;21;KG +10;14;KG diff --git a/src/main/resources/config/liquibase/fake-data/weight.csv b/src/main/resources/config/liquibase/fake-data/weight.csv index 8f124f83..1ef6f3da 100644 --- a/src/main/resources/config/liquibase/fake-data/weight.csv +++ b/src/main/resources/config/liquibase/fake-data/weight.csv @@ -1,11 +1,11 @@ id;timestamp;weight -1;2022-11-07T09:19:51;32832 -2;2022-11-07T19:14:33;17894 -3;2022-11-07T18:55:08;44583 -4;2022-11-07T22:37:56;2935 -5;2022-11-07T09:19:52;7847 -6;2022-11-07T12:03:37;23548 -7;2022-11-07T19:32:26;8714 -8;2022-11-07T06:29:06;620 -9;2022-11-07T03:01:10;92367 -10;2022-11-07T03:16:38;15689 +1;2022-11-07T00:20:37;201.83 +2;2022-11-07T23:08:13;32653.85 +3;2022-11-07T01:33:17;4276.15 +4;2022-11-07T11:04:01;15376.65 +5;2022-11-07T09:32:02;28621.12 +6;2022-11-07T14:15:14;12663.9 +7;2022-11-07T02:44:11;22824.91 +8;2022-11-07T19:27:25;11085.23 +9;2022-11-07T06:20:50;21063.82 +10;2022-11-07T22:19:16;6318.41 diff --git a/src/main/resources/config/liquibase/master.xml b/src/main/resources/config/liquibase/master.xml index 5c827d43..01053bbd 100644 --- a/src/main/resources/config/liquibase/master.xml +++ b/src/main/resources/config/liquibase/master.xml @@ -6,13 +6,13 @@ - + - - + + diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml index c5ccc030..d0f1158b 100644 --- a/src/main/resources/logback-spring.xml +++ b/src/main/resources/logback-spring.xml @@ -2,25 +2,20 @@ - + - + + - + + - - + + --> + + - - - - - + + + + + + @@ -55,22 +53,25 @@ + - - + + - - + + + + true diff --git a/src/main/webapp/app/account/account.module.ts b/src/main/webapp/app/account/account.module.ts deleted file mode 100644 index 060a020a..00000000 --- a/src/main/webapp/app/account/account.module.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { NgModule } from '@angular/core'; -import { RouterModule } from '@angular/router'; - -import { SharedModule } from 'app/shared/shared.module'; -import { PasswordStrengthBarComponent } from './password/password-strength-bar/password-strength-bar.component'; -import { RegisterComponent } from './register/register.component'; -import { ActivateComponent } from './activate/activate.component'; -import { PasswordComponent } from './password/password.component'; -import { PasswordResetInitComponent } from './password-reset/init/password-reset-init.component'; -import { PasswordResetFinishComponent } from './password-reset/finish/password-reset-finish.component'; -import { SettingsComponent } from './settings/settings.component'; -import { accountState } from './account.route'; - -@NgModule({ - imports: [SharedModule, RouterModule.forChild(accountState)], - declarations: [ - ActivateComponent, - RegisterComponent, - PasswordComponent, - PasswordStrengthBarComponent, - PasswordResetInitComponent, - PasswordResetFinishComponent, - SettingsComponent, - ], -}) -export class AccountModule {} diff --git a/src/main/webapp/app/account/account.route.ts b/src/main/webapp/app/account/account.route.ts index 1cbfb9d0..a1a8b462 100644 --- a/src/main/webapp/app/account/account.route.ts +++ b/src/main/webapp/app/account/account.route.ts @@ -1,17 +1,19 @@ import { Routes } from '@angular/router'; -import { activateRoute } from './activate/activate.route'; -import { passwordRoute } from './password/password.route'; -import { passwordResetFinishRoute } from './password-reset/finish/password-reset-finish.route'; -import { passwordResetInitRoute } from './password-reset/init/password-reset-init.route'; -import { registerRoute } from './register/register.route'; -import { settingsRoute } from './settings/settings.route'; +import activateRoute from './activate/activate.route'; +import passwordRoute from './password/password.route'; +import passwordResetFinishRoute from './password-reset/finish/password-reset-finish.route'; +import passwordResetInitRoute from './password-reset/init/password-reset-init.route'; +import registerRoute from './register/register.route'; +import settingsRoute from './settings/settings.route'; -const ACCOUNT_ROUTES = [activateRoute, passwordRoute, passwordResetFinishRoute, passwordResetInitRoute, registerRoute, settingsRoute]; - -export const accountState: Routes = [ - { - path: '', - children: ACCOUNT_ROUTES, - }, +const accountRoutes: Routes = [ + activateRoute, + passwordRoute, + passwordResetFinishRoute, + passwordResetInitRoute, + registerRoute, + settingsRoute, ]; + +export default accountRoutes; diff --git a/src/main/webapp/app/account/activate/activate.component.html b/src/main/webapp/app/account/activate/activate.component.html index 4cd58373..93a51846 100644 --- a/src/main/webapp/app/account/activate/activate.component.html +++ b/src/main/webapp/app/account/activate/activate.component.html @@ -2,15 +2,17 @@

Activation

- -
- Your user account has been activated. Please - sign in. -
- -
- Your user could not be activated. Please use the registration form to sign up. -
+ @if (success()) { +
+ Your user account has been activated. Please + sign in. +
+ } + @if (error()) { +
+ Your user could not be activated. Please use the registration form to sign up. +
+ }
diff --git a/src/main/webapp/app/account/activate/activate.component.spec.ts b/src/main/webapp/app/account/activate/activate.component.spec.ts index 4a31d0b7..c99a0fab 100644 --- a/src/main/webapp/app/account/activate/activate.component.spec.ts +++ b/src/main/webapp/app/account/activate/activate.component.spec.ts @@ -4,15 +4,14 @@ import { ActivatedRoute } from '@angular/router'; import { of, throwError } from 'rxjs'; import { ActivateService } from './activate.service'; -import { ActivateComponent } from './activate.component'; +import ActivateComponent from './activate.component'; describe('ActivateComponent', () => { let comp: ActivateComponent; beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ - imports: [HttpClientTestingModule], - declarations: [ActivateComponent], + imports: [HttpClientTestingModule, ActivateComponent], providers: [ { provide: ActivatedRoute, @@ -49,8 +48,8 @@ describe('ActivateComponent', () => { comp.ngOnInit(); tick(); - expect(comp.error).toBe(false); - expect(comp.success).toBe(true); + expect(comp.error()).toBe(false); + expect(comp.success()).toBe(true); }), )); @@ -62,8 +61,8 @@ describe('ActivateComponent', () => { comp.ngOnInit(); tick(); - expect(comp.error).toBe(true); - expect(comp.success).toBe(false); + expect(comp.error()).toBe(true); + expect(comp.success()).toBe(false); }), )); }); diff --git a/src/main/webapp/app/account/activate/activate.component.ts b/src/main/webapp/app/account/activate/activate.component.ts index 8250a0b3..ab333c0b 100644 --- a/src/main/webapp/app/account/activate/activate.component.ts +++ b/src/main/webapp/app/account/activate/activate.component.ts @@ -1,26 +1,27 @@ -import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; +import { Component, inject, OnInit, signal } from '@angular/core'; +import { ActivatedRoute, RouterModule } from '@angular/router'; import { mergeMap } from 'rxjs/operators'; +import SharedModule from 'app/shared/shared.module'; import { ActivateService } from './activate.service'; @Component({ + standalone: true, selector: 'jhi-activate', + imports: [SharedModule, RouterModule], templateUrl: './activate.component.html', }) -export class ActivateComponent implements OnInit { - error = false; - success = false; +export default class ActivateComponent implements OnInit { + error = signal(false); + success = signal(false); - constructor( - private activateService: ActivateService, - private route: ActivatedRoute, - ) {} + private activateService = inject(ActivateService); + private route = inject(ActivatedRoute); ngOnInit(): void { this.route.queryParams.pipe(mergeMap(params => this.activateService.get(params.key))).subscribe({ - next: () => (this.success = true), - error: () => (this.error = true), + next: () => this.success.set(true), + error: () => this.error.set(true), }); } } diff --git a/src/main/webapp/app/account/activate/activate.route.ts b/src/main/webapp/app/account/activate/activate.route.ts index 8da1b6d5..5f602552 100644 --- a/src/main/webapp/app/account/activate/activate.route.ts +++ b/src/main/webapp/app/account/activate/activate.route.ts @@ -1,11 +1,11 @@ import { Route } from '@angular/router'; -import { ActivateComponent } from './activate.component'; +import ActivateComponent from './activate.component'; -export const activateRoute: Route = { +const activateRoute: Route = { path: 'activate', component: ActivateComponent, - data: { - pageTitle: 'activate.title', - }, + title: 'activate.title', }; + +export default activateRoute; diff --git a/src/main/webapp/app/account/activate/activate.service.ts b/src/main/webapp/app/account/activate/activate.service.ts index 9e19475b..58315580 100644 --- a/src/main/webapp/app/account/activate/activate.service.ts +++ b/src/main/webapp/app/account/activate/activate.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { inject, Injectable } from '@angular/core'; import { HttpClient, HttpParams } from '@angular/common/http'; import { Observable } from 'rxjs'; @@ -6,10 +6,8 @@ import { ApplicationConfigService } from 'app/core/config/application-config.ser @Injectable({ providedIn: 'root' }) export class ActivateService { - constructor( - private http: HttpClient, - private applicationConfigService: ApplicationConfigService, - ) {} + private http = inject(HttpClient); + private applicationConfigService = inject(ApplicationConfigService); get(key: string): Observable<{}> { return this.http.get(this.applicationConfigService.getEndpointFor('api/activate'), { diff --git a/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.html b/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.html index 493ef8f9..fa4dbf1e 100644 --- a/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.html +++ b/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.html @@ -3,135 +3,131 @@

Reset password

-
- The reset key is missing. -
+ @if (initialized() && !key()) { +
The reset key is missing.
+ } -
- Choose a new password -
+ @if (key() && !success()) { +
+ Choose a new password +
+ } -
- Your password couldn't be reset. Remember a password request is only valid for 24 hours. -
+ @if (error()) { +
+ Your password couldn't be reset. Remember a password request is only valid for 24 hours. +
+ } -
- Your password has been reset. Please - sign in. -
+ @if (success()) { +
+ Your password has been reset. Please + sign in. +
+ } -
- The password and its confirmation do not match! -
+ @if (doNotMatch()) { +
The password and its confirmation do not match!
+ } -
-
-
- - + @if (key() && !success()) { +
+ +
+ + -
- - Your password is required. - + ) { +
+ @if (passwordForm.get('newPassword')?.errors?.required) { + Your password is required. + } - - Your password is required to be at least 4 characters. - + @if (passwordForm.get('newPassword')?.errors?.minlength) { + Your password is required to be at least 4 characters. + } - - Your password cannot be longer than 50 characters. - -
+ @if (passwordForm.get('newPassword')?.errors?.maxlength) { + Your password cannot be longer than 50 characters. + } +
+ } - -
+ +
-
- - +
+ + -
- - Your confirmation password is required. - + ) { +
+ @if (passwordForm.get('confirmPassword')?.errors?.required) { + Your confirmation password is required. + } - - Your confirmation password is required to be at least 4 characters. - + @if (passwordForm.get('confirmPassword')?.errors?.minlength) { + Your confirmation password is required to be at least 4 characters. + } - - Your confirmation password cannot be longer than 50 characters. - + @if (passwordForm.get('confirmPassword')?.errors?.maxlength) { + Your confirmation password cannot be longer than 50 characters. + } +
+ }
-
- - -
+ + +
+ }
diff --git a/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.spec.ts b/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.spec.ts index 50419759..aa229f01 100644 --- a/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.spec.ts +++ b/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.spec.ts @@ -5,7 +5,7 @@ import { FormBuilder } from '@angular/forms'; import { ActivatedRoute } from '@angular/router'; import { of, throwError } from 'rxjs'; -import { PasswordResetFinishComponent } from './password-reset-finish.component'; +import PasswordResetFinishComponent from './password-reset-finish.component'; import { PasswordResetFinishService } from './password-reset-finish.service'; describe('PasswordResetFinishComponent', () => { @@ -14,8 +14,7 @@ describe('PasswordResetFinishComponent', () => { beforeEach(() => { fixture = TestBed.configureTestingModule({ - imports: [HttpClientTestingModule], - declarations: [PasswordResetFinishComponent], + imports: [HttpClientTestingModule, PasswordResetFinishComponent], providers: [ FormBuilder, { @@ -35,8 +34,8 @@ describe('PasswordResetFinishComponent', () => { }); it('should define its initial state', () => { - expect(comp.initialized).toBe(true); - expect(comp.key).toEqual('XYZPDQ'); + expect(comp.initialized()).toBe(true); + expect(comp.key()).toEqual('XYZPDQ'); }); it('sets focus after the view has been initialized', () => { @@ -58,7 +57,7 @@ describe('PasswordResetFinishComponent', () => { comp.finishReset(); - expect(comp.doNotMatch).toBe(true); + expect(comp.doNotMatch()).toBe(true); }); it('should update success to true after resetting password', inject( @@ -74,7 +73,7 @@ describe('PasswordResetFinishComponent', () => { tick(); expect(service.save).toHaveBeenCalledWith('XYZPDQ', 'password'); - expect(comp.success).toBe(true); + expect(comp.success()).toBe(true); }), )); @@ -91,8 +90,8 @@ describe('PasswordResetFinishComponent', () => { tick(); expect(service.save).toHaveBeenCalledWith('XYZPDQ', 'password'); - expect(comp.success).toBe(false); - expect(comp.error).toBe(true); + expect(comp.success()).toBe(false); + expect(comp.error()).toBe(true); }), )); }); diff --git a/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.ts b/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.ts index 11179cb0..ba9295ad 100644 --- a/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.ts +++ b/src/main/webapp/app/account/password-reset/finish/password-reset-finish.component.ts @@ -1,22 +1,26 @@ -import { Component, OnInit, AfterViewInit, ElementRef, ViewChild } from '@angular/core'; -import { FormGroup, FormControl, Validators } from '@angular/forms'; -import { ActivatedRoute } from '@angular/router'; +import { Component, inject, OnInit, AfterViewInit, ElementRef, ViewChild, signal } from '@angular/core'; +import { FormGroup, FormControl, Validators, FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { ActivatedRoute, RouterModule } from '@angular/router'; +import PasswordStrengthBarComponent from 'app/account/password/password-strength-bar/password-strength-bar.component'; +import SharedModule from 'app/shared/shared.module'; import { PasswordResetFinishService } from './password-reset-finish.service'; @Component({ + standalone: true, selector: 'jhi-password-reset-finish', + imports: [SharedModule, RouterModule, FormsModule, ReactiveFormsModule, PasswordStrengthBarComponent], templateUrl: './password-reset-finish.component.html', }) -export class PasswordResetFinishComponent implements OnInit, AfterViewInit { +export default class PasswordResetFinishComponent implements OnInit, AfterViewInit { @ViewChild('newPassword', { static: false }) newPassword?: ElementRef; - initialized = false; - doNotMatch = false; - error = false; - success = false; - key = ''; + initialized = signal(false); + doNotMatch = signal(false); + error = signal(false); + success = signal(false); + key = signal(''); passwordForm = new FormGroup({ newPassword: new FormControl('', { @@ -29,17 +33,15 @@ export class PasswordResetFinishComponent implements OnInit, AfterViewInit { }), }); - constructor( - private passwordResetFinishService: PasswordResetFinishService, - private route: ActivatedRoute, - ) {} + private passwordResetFinishService = inject(PasswordResetFinishService); + private route = inject(ActivatedRoute); ngOnInit(): void { this.route.queryParams.subscribe(params => { if (params['key']) { - this.key = params['key']; + this.key.set(params['key']); } - this.initialized = true; + this.initialized.set(true); }); } @@ -50,17 +52,17 @@ export class PasswordResetFinishComponent implements OnInit, AfterViewInit { } finishReset(): void { - this.doNotMatch = false; - this.error = false; + this.doNotMatch.set(false); + this.error.set(false); const { newPassword, confirmPassword } = this.passwordForm.getRawValue(); if (newPassword !== confirmPassword) { - this.doNotMatch = true; + this.doNotMatch.set(true); } else { - this.passwordResetFinishService.save(this.key, newPassword).subscribe({ - next: () => (this.success = true), - error: () => (this.error = true), + this.passwordResetFinishService.save(this.key(), newPassword).subscribe({ + next: () => this.success.set(true), + error: () => this.error.set(true), }); } } diff --git a/src/main/webapp/app/account/password-reset/finish/password-reset-finish.route.ts b/src/main/webapp/app/account/password-reset/finish/password-reset-finish.route.ts index 3786df74..2963d150 100644 --- a/src/main/webapp/app/account/password-reset/finish/password-reset-finish.route.ts +++ b/src/main/webapp/app/account/password-reset/finish/password-reset-finish.route.ts @@ -1,11 +1,11 @@ import { Route } from '@angular/router'; -import { PasswordResetFinishComponent } from './password-reset-finish.component'; +import PasswordResetFinishComponent from './password-reset-finish.component'; -export const passwordResetFinishRoute: Route = { +const passwordResetFinishRoute: Route = { path: 'reset/finish', component: PasswordResetFinishComponent, - data: { - pageTitle: 'global.menu.account.password', - }, + title: 'global.menu.account.password', }; + +export default passwordResetFinishRoute; diff --git a/src/main/webapp/app/account/password-reset/finish/password-reset-finish.service.ts b/src/main/webapp/app/account/password-reset/finish/password-reset-finish.service.ts index 3696e10b..f3aa78b3 100644 --- a/src/main/webapp/app/account/password-reset/finish/password-reset-finish.service.ts +++ b/src/main/webapp/app/account/password-reset/finish/password-reset-finish.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { inject, Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; @@ -6,10 +6,8 @@ import { ApplicationConfigService } from 'app/core/config/application-config.ser @Injectable({ providedIn: 'root' }) export class PasswordResetFinishService { - constructor( - private http: HttpClient, - private applicationConfigService: ApplicationConfigService, - ) {} + private http = inject(HttpClient); + private applicationConfigService = inject(ApplicationConfigService); save(key: string, newPassword: string): Observable<{}> { return this.http.post(this.applicationConfigService.getEndpointFor('api/account/reset-password/finish'), { key, newPassword }); diff --git a/src/main/webapp/app/account/password-reset/init/password-reset-init.component.html b/src/main/webapp/app/account/password-reset/init/password-reset-init.component.html index 301a96e9..acc22e8b 100644 --- a/src/main/webapp/app/account/password-reset/init/password-reset-init.component.html +++ b/src/main/webapp/app/account/password-reset/init/password-reset-init.component.html @@ -5,77 +5,67 @@

Reset your password

-
- Enter the email address you used to register -
- -
- Check your emails for details on how to reset your password. -
- -
-
- - + @if (!success()) { +
+ Enter the email address you used to register +
+ +
+ + -
- - Your email is required. - + ) { +
+ @if (resetRequestForm.get('email')?.errors?.required) { + Your email is required. + } + @if (resetRequestForm.get('email')?.errors?.email) { + Your email is invalid. + } - - Your email is invalid. - + @if (resetRequestForm.get('email')?.errors?.minlength) { + Your email is required to be at least 5 characters. + } - - Your email is required to be at least 5 characters. - - - - Your email cannot be longer than 50 characters. - + @if (resetRequestForm.get('email')?.errors?.maxlength) { + Your email cannot be longer than 50 characters. + } +
+ }
-
- - + + + } @else { +
+ Check your email for details on how to reset your password. +
+ }
diff --git a/src/main/webapp/app/account/password-reset/init/password-reset-init.component.spec.ts b/src/main/webapp/app/account/password-reset/init/password-reset-init.component.spec.ts index ebc6ef8b..47f6e5b6 100644 --- a/src/main/webapp/app/account/password-reset/init/password-reset-init.component.spec.ts +++ b/src/main/webapp/app/account/password-reset/init/password-reset-init.component.spec.ts @@ -4,7 +4,7 @@ import { HttpClientTestingModule } from '@angular/common/http/testing'; import { FormBuilder } from '@angular/forms'; import { of, throwError } from 'rxjs'; -import { PasswordResetInitComponent } from './password-reset-init.component'; +import PasswordResetInitComponent from './password-reset-init.component'; import { PasswordResetInitService } from './password-reset-init.service'; describe('PasswordResetInitComponent', () => { @@ -13,8 +13,7 @@ describe('PasswordResetInitComponent', () => { beforeEach(() => { fixture = TestBed.configureTestingModule({ - imports: [HttpClientTestingModule], - declarations: [PasswordResetInitComponent], + imports: [HttpClientTestingModule, PasswordResetInitComponent], providers: [FormBuilder], }) .overrideTemplate(PasswordResetInitComponent, '') @@ -42,22 +41,18 @@ describe('PasswordResetInitComponent', () => { comp.requestReset(); expect(service.save).toHaveBeenCalledWith('user@domain.com'); - expect(comp.success).toBe(true); + expect(comp.success()).toBe(true); })); it('no notification of success upon error response', inject([PasswordResetInitService], (service: PasswordResetInitService) => { - jest.spyOn(service, 'save').mockReturnValue( - throwError({ - status: 503, - data: 'something else', - }), - ); + const err = { status: 503, data: 'something else' }; + jest.spyOn(service, 'save').mockReturnValue(throwError(() => err)); comp.resetRequestForm.patchValue({ email: 'user@domain.com', }); comp.requestReset(); expect(service.save).toHaveBeenCalledWith('user@domain.com'); - expect(comp.success).toBe(false); + expect(comp.success()).toBe(false); })); }); diff --git a/src/main/webapp/app/account/password-reset/init/password-reset-init.component.ts b/src/main/webapp/app/account/password-reset/init/password-reset-init.component.ts index 06060126..af68346c 100644 --- a/src/main/webapp/app/account/password-reset/init/password-reset-init.component.ts +++ b/src/main/webapp/app/account/password-reset/init/password-reset-init.component.ts @@ -1,25 +1,30 @@ -import { Component, AfterViewInit, ElementRef, ViewChild } from '@angular/core'; -import { FormBuilder, Validators } from '@angular/forms'; +import { Component, AfterViewInit, ElementRef, inject, ViewChild, signal } from '@angular/core'; +import { FormBuilder, FormsModule, ReactiveFormsModule, Validators } from '@angular/forms'; +import SharedModule from 'app/shared/shared.module'; import { PasswordResetInitService } from './password-reset-init.service'; @Component({ + standalone: true, selector: 'jhi-password-reset-init', + imports: [SharedModule, FormsModule, ReactiveFormsModule], templateUrl: './password-reset-init.component.html', }) -export class PasswordResetInitComponent implements AfterViewInit { +export default class PasswordResetInitComponent implements AfterViewInit { @ViewChild('email', { static: false }) email?: ElementRef; - success = false; - resetRequestForm = this.fb.group({ - email: ['', [Validators.required, Validators.minLength(5), Validators.maxLength(254), Validators.email]], - }); + success = signal(false); + resetRequestForm; - constructor( - private passwordResetInitService: PasswordResetInitService, - private fb: FormBuilder, - ) {} + private passwordResetInitService = inject(PasswordResetInitService); + private fb = inject(FormBuilder); + + constructor() { + this.resetRequestForm = this.fb.group({ + email: ['', [Validators.required, Validators.minLength(5), Validators.maxLength(254), Validators.email]], + }); + } ngAfterViewInit(): void { if (this.email) { @@ -28,6 +33,6 @@ export class PasswordResetInitComponent implements AfterViewInit { } requestReset(): void { - this.passwordResetInitService.save(this.resetRequestForm.get(['email'])!.value).subscribe(() => (this.success = true)); + this.passwordResetInitService.save(this.resetRequestForm.get(['email'])!.value).subscribe(() => this.success.set(true)); } } diff --git a/src/main/webapp/app/account/password-reset/init/password-reset-init.route.ts b/src/main/webapp/app/account/password-reset/init/password-reset-init.route.ts index 6ba92a41..654634be 100644 --- a/src/main/webapp/app/account/password-reset/init/password-reset-init.route.ts +++ b/src/main/webapp/app/account/password-reset/init/password-reset-init.route.ts @@ -1,11 +1,11 @@ import { Route } from '@angular/router'; -import { PasswordResetInitComponent } from './password-reset-init.component'; +import PasswordResetInitComponent from './password-reset-init.component'; -export const passwordResetInitRoute: Route = { +const passwordResetInitRoute: Route = { path: 'reset/request', component: PasswordResetInitComponent, - data: { - pageTitle: 'global.menu.account.password', - }, + title: 'global.menu.account.password', }; + +export default passwordResetInitRoute; diff --git a/src/main/webapp/app/account/password-reset/init/password-reset-init.service.ts b/src/main/webapp/app/account/password-reset/init/password-reset-init.service.ts index 31ed7e89..9c0cddd8 100644 --- a/src/main/webapp/app/account/password-reset/init/password-reset-init.service.ts +++ b/src/main/webapp/app/account/password-reset/init/password-reset-init.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { inject, Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; @@ -6,10 +6,8 @@ import { ApplicationConfigService } from 'app/core/config/application-config.ser @Injectable({ providedIn: 'root' }) export class PasswordResetInitService { - constructor( - private http: HttpClient, - private applicationConfigService: ApplicationConfigService, - ) {} + private http = inject(HttpClient); + private applicationConfigService = inject(ApplicationConfigService); save(mail: string): Observable<{}> { return this.http.post(this.applicationConfigService.getEndpointFor('api/account/reset-password/init'), mail); diff --git a/src/main/webapp/app/account/password/password-strength-bar/password-strength-bar.component.spec.ts b/src/main/webapp/app/account/password/password-strength-bar/password-strength-bar.component.spec.ts index 0ff9dbda..9fd533a7 100644 --- a/src/main/webapp/app/account/password/password-strength-bar/password-strength-bar.component.spec.ts +++ b/src/main/webapp/app/account/password/password-strength-bar/password-strength-bar.component.spec.ts @@ -1,6 +1,6 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; -import { PasswordStrengthBarComponent } from './password-strength-bar.component'; +import PasswordStrengthBarComponent from './password-strength-bar.component'; describe('PasswordStrengthBarComponent', () => { let comp: PasswordStrengthBarComponent; @@ -8,7 +8,7 @@ describe('PasswordStrengthBarComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ - declarations: [PasswordStrengthBarComponent], + imports: [PasswordStrengthBarComponent], }) .overrideTemplate(PasswordStrengthBarComponent, '') .compileComponents(); diff --git a/src/main/webapp/app/account/password/password-strength-bar/password-strength-bar.component.ts b/src/main/webapp/app/account/password/password-strength-bar/password-strength-bar.component.ts index 700008fd..9ffd735e 100644 --- a/src/main/webapp/app/account/password/password-strength-bar/password-strength-bar.component.ts +++ b/src/main/webapp/app/account/password/password-strength-bar/password-strength-bar.component.ts @@ -1,17 +1,19 @@ -import { Component, ElementRef, Input, Renderer2 } from '@angular/core'; +import { Component, ElementRef, inject, Input, Renderer2 } from '@angular/core'; + +import SharedModule from 'app/shared/shared.module'; @Component({ + standalone: true, selector: 'jhi-password-strength-bar', + imports: [SharedModule], templateUrl: './password-strength-bar.component.html', - styleUrls: ['./password-strength-bar.component.scss'], + styleUrl: './password-strength-bar.component.scss', }) -export class PasswordStrengthBarComponent { +export default class PasswordStrengthBarComponent { colors = ['#F00', '#F90', '#FF0', '#9F0', '#0F0']; - constructor( - private renderer: Renderer2, - private elementRef: ElementRef, - ) {} + private renderer = inject(Renderer2); + private elementRef = inject(ElementRef); measureStrength(p: string): number { let force = 0; diff --git a/src/main/webapp/app/account/password/password.component.html b/src/main/webapp/app/account/password/password.component.html index 6584df72..1466f9ef 100644 --- a/src/main/webapp/app/account/password/password.component.html +++ b/src/main/webapp/app/account/password/password.component.html @@ -1,152 +1,144 @@
-
-

- Password for [{{ account.login }}] -

+ @if (account$ | async; as account) { +
+

+ Password for [{{ account.login }}] +

-
- Password changed! -
- -
- An error has occurred! The password could not be changed. -
- -
- The password and its confirmation do not match! -
+ @if (success()) { +
Password changed!
+ } + @if (error()) { +
+ An error has occurred! The password could not be changed. +
+ } + @if (doNotMatch()) { +
+ The password and its confirmation do not match! +
+ } -
-
- - + +
+ + -
- - Your password is required. - + ) { +
+ @if (passwordForm.get('currentPassword')?.errors?.required) { + Your password is required. + } +
+ }
-
-
- - +
+ + -
- - Your password is required. - + ) { +
+ @if (passwordForm.get('newPassword')?.errors?.required) { + Your password is required. + } - - Your password is required to be at least 4 characters. - + @if (passwordForm.get('newPassword')?.errors?.minlength) { + Your password is required to be at least 4 characters. + } - - Your password cannot be longer than 50 characters. - -
+ @if (passwordForm.get('newPassword')?.errors?.maxlength) { + Your password cannot be longer than 50 characters. + } +
+ } - -
+ +
-
- - +
+ + -
- - Your confirmation password is required. - + ) { +
+ @if (passwordForm.get('confirmPassword')?.errors?.required) { + Your confirmation password is required. + } - - Your confirmation password is required to be at least 4 characters. - + @if (passwordForm.get('confirmPassword')?.errors?.minlength) { + Your confirmation password is required to be at least 4 characters. + } - - Your confirmation password cannot be longer than 50 characters. - + @if (passwordForm.get('confirmPassword')?.errors?.maxlength) { + Your confirmation password cannot be longer than 50 characters. + } +
+ }
-
- - -
+ + +
+ }
diff --git a/src/main/webapp/app/account/password/password.component.spec.ts b/src/main/webapp/app/account/password/password.component.spec.ts index 7ad52b4a..d82d46c6 100644 --- a/src/main/webapp/app/account/password/password.component.spec.ts +++ b/src/main/webapp/app/account/password/password.component.spec.ts @@ -8,7 +8,7 @@ import { of, throwError } from 'rxjs'; import { AccountService } from 'app/core/auth/account.service'; -import { PasswordComponent } from './password.component'; +import PasswordComponent from './password.component'; import { PasswordService } from './password.service'; describe('PasswordComponent', () => { @@ -18,8 +18,7 @@ describe('PasswordComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ - imports: [HttpClientTestingModule], - declarations: [PasswordComponent], + imports: [HttpClientTestingModule, PasswordComponent], providers: [FormBuilder, AccountService], }) .overrideTemplate(PasswordComponent, '') @@ -41,9 +40,9 @@ describe('PasswordComponent', () => { // WHEN comp.changePassword(); // THEN - expect(comp.doNotMatch).toBe(true); - expect(comp.error).toBe(false); - expect(comp.success).toBe(false); + expect(comp.doNotMatch()).toBe(true); + expect(comp.error()).toBe(false); + expect(comp.success()).toBe(false); }); it('should call Auth.changePassword when passwords match', () => { @@ -80,9 +79,9 @@ describe('PasswordComponent', () => { comp.changePassword(); // THEN - expect(comp.doNotMatch).toBe(false); - expect(comp.error).toBe(false); - expect(comp.success).toBe(true); + expect(comp.doNotMatch()).toBe(false); + expect(comp.error()).toBe(false); + expect(comp.success()).toBe(true); }); it('should notify of error if change password fails', () => { @@ -97,8 +96,8 @@ describe('PasswordComponent', () => { comp.changePassword(); // THEN - expect(comp.doNotMatch).toBe(false); - expect(comp.success).toBe(false); - expect(comp.error).toBe(true); + expect(comp.doNotMatch()).toBe(false); + expect(comp.success()).toBe(false); + expect(comp.error()).toBe(true); }); }); diff --git a/src/main/webapp/app/account/password/password.component.ts b/src/main/webapp/app/account/password/password.component.ts index 3150753e..3338a16c 100644 --- a/src/main/webapp/app/account/password/password.component.ts +++ b/src/main/webapp/app/account/password/password.component.ts @@ -1,19 +1,23 @@ -import { Component, OnInit } from '@angular/core'; -import { FormGroup, FormControl, Validators } from '@angular/forms'; +import { Component, inject, OnInit, signal } from '@angular/core'; +import { FormGroup, FormControl, Validators, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { Observable } from 'rxjs'; +import SharedModule from 'app/shared/shared.module'; import { AccountService } from 'app/core/auth/account.service'; import { Account } from 'app/core/auth/account.model'; import { PasswordService } from './password.service'; +import PasswordStrengthBarComponent from './password-strength-bar/password-strength-bar.component'; @Component({ + standalone: true, selector: 'jhi-password', + imports: [SharedModule, FormsModule, ReactiveFormsModule, PasswordStrengthBarComponent], templateUrl: './password.component.html', }) -export class PasswordComponent implements OnInit { - doNotMatch = false; - error = false; - success = false; +export default class PasswordComponent implements OnInit { + doNotMatch = signal(false); + error = signal(false); + success = signal(false); account$?: Observable; passwordForm = new FormGroup({ currentPassword: new FormControl('', { nonNullable: true, validators: Validators.required }), @@ -27,27 +31,25 @@ export class PasswordComponent implements OnInit { }), }); - constructor( - private passwordService: PasswordService, - private accountService: AccountService, - ) {} + private passwordService = inject(PasswordService); + private accountService = inject(AccountService); ngOnInit(): void { this.account$ = this.accountService.identity(); } changePassword(): void { - this.error = false; - this.success = false; - this.doNotMatch = false; + this.error.set(false); + this.success.set(false); + this.doNotMatch.set(false); const { newPassword, confirmPassword, currentPassword } = this.passwordForm.getRawValue(); if (newPassword !== confirmPassword) { - this.doNotMatch = true; + this.doNotMatch.set(true); } else { this.passwordService.save(newPassword, currentPassword).subscribe({ - next: () => (this.success = true), - error: () => (this.error = true), + next: () => this.success.set(true), + error: () => this.error.set(true), }); } } diff --git a/src/main/webapp/app/account/password/password.route.ts b/src/main/webapp/app/account/password/password.route.ts index 174b54f5..96faba98 100644 --- a/src/main/webapp/app/account/password/password.route.ts +++ b/src/main/webapp/app/account/password/password.route.ts @@ -1,13 +1,13 @@ import { Route } from '@angular/router'; import { UserRouteAccessService } from 'app/core/auth/user-route-access.service'; -import { PasswordComponent } from './password.component'; +import PasswordComponent from './password.component'; -export const passwordRoute: Route = { +const passwordRoute: Route = { path: 'password', component: PasswordComponent, - data: { - pageTitle: 'global.menu.account.password', - }, + title: 'global.menu.account.password', canActivate: [UserRouteAccessService], }; + +export default passwordRoute; diff --git a/src/main/webapp/app/account/password/password.service.ts b/src/main/webapp/app/account/password/password.service.ts index e29258f5..3bfc0a2f 100644 --- a/src/main/webapp/app/account/password/password.service.ts +++ b/src/main/webapp/app/account/password/password.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { inject, Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; @@ -6,10 +6,8 @@ import { ApplicationConfigService } from 'app/core/config/application-config.ser @Injectable({ providedIn: 'root' }) export class PasswordService { - constructor( - private http: HttpClient, - private applicationConfigService: ApplicationConfigService, - ) {} + private http = inject(HttpClient); + private applicationConfigService = inject(ApplicationConfigService); save(newPassword: string, currentPassword: string): Observable<{}> { return this.http.post(this.applicationConfigService.getEndpointFor('api/account/change-password'), { currentPassword, newPassword }); diff --git a/src/main/webapp/app/account/register/register.component.html b/src/main/webapp/app/account/register/register.component.html index c9eb2af9..b3fe5d71 100644 --- a/src/main/webapp/app/account/register/register.component.html +++ b/src/main/webapp/app/account/register/register.component.html @@ -1,232 +1,218 @@
-

Registration

+

Registration

-
- Registration saved! Please check your email for confirmation. -
+ @if (success()) { +
+ Registration saved! Please check your email for confirmation. +
+ } -
- Registration failed! Please try again later. -
+ @if (error()) { +
+ Registration failed! Please try again later. +
+ } -
- Login name already registered! Please choose another one. -
+ @if (errorUserExists()) { +
+ Login name already registered! Please choose another one. +
+ } -
- Email is already in use! Please choose another one. -
+ @if (errorEmailExists()) { +
+ Email is already in use! Please choose another one. +
+ } -
- The password and its confirmation do not match! -
+ @if (doNotMatch()) { +
The password and its confirmation do not match!
+ }
-
-
- - - -
- - Your username is required. - - - - Your username is required to be at least 1 character. - - - - Your username cannot be longer than 50 characters. - - - - Your username is invalid. - + @if (!success()) { + +
+ + + + @if (registerForm.get('login')!.invalid && (registerForm.get('login')!.dirty || registerForm.get('login')!.touched)) { +
+ @if (registerForm.get('login')?.errors?.required) { + Your username is required. + } + + @if (registerForm.get('login')?.errors?.minlength) { + Your username is required to be at least 1 character. + } + + @if (registerForm.get('login')?.errors?.maxlength) { + Your username cannot be longer than 50 characters. + } + + @if (registerForm.get('login')?.errors?.pattern) { + Your username is invalid. + } +
+ }
-
-
- - - -
- - Your email is required. - - - - Your email is invalid. - - - - Your email is required to be at least 5 characters. - - - - Your email cannot be longer than 50 characters. - +
+ + + + @if (registerForm.get('email')!.invalid && (registerForm.get('email')!.dirty || registerForm.get('email')!.touched)) { +
+ @if (registerForm.get('email')?.errors?.required) { + Your email is required. + } + + @if (registerForm.get('email')?.errors?.invalid) { + Your email is invalid. + } + + @if (registerForm.get('email')?.errors?.minlength) { + Your email is required to be at least 5 characters. + } + + @if (registerForm.get('email')?.errors?.maxlength) { + Your email cannot be longer than 50 characters. + } +
+ }
-
- -
- - - -
- - Your password is required. - - - Your password is required to be at least 4 characters. - - - - Your password cannot be longer than 50 characters. - +
+ + + + @if (registerForm.get('password')!.invalid && (registerForm.get('password')!.dirty || registerForm.get('password')!.touched)) { +
+ @if (registerForm.get('password')?.errors?.required) { + Your password is required. + } + + @if (registerForm.get('password')?.errors?.minlength) { + Your password is required to be at least 4 characters. + } + + @if (registerForm.get('password')?.errors?.maxlength) { + Your password cannot be longer than 50 characters. + } +
+ } + +
- -
- -
- - - -
+ + + + @if ( registerForm.get('confirmPassword')!.invalid && (registerForm.get('confirmPassword')!.dirty || registerForm.get('confirmPassword')!.touched) - " - > - - Your confirmation password is required. - - - - Your confirmation password is required to be at least 4 characters. - - - - Your confirmation password cannot be longer than 50 characters. - + ) { +
+ @if (registerForm.get('confirmPassword')?.errors?.required) { + Your confirmation password is required. + } + + @if (registerForm.get('confirmPassword')?.errors?.minlength) { + Your confirmation password is required to be at least 4 characters. + } + + @if (registerForm.get('confirmPassword')?.errors?.maxlength) { + Your confirmation password cannot be longer than 50 characters. + } +
+ }
-
- - + + + }
If you want to sign in, you can try the default accounts:
- Administrator (login="admin" and password="admin")
- User (login="user" and - password="user").
, you can try the default accounts:
- Administrator (login="admin" and password="admin")
- User + (login="user" and password="user").
diff --git a/src/main/webapp/app/account/register/register.component.spec.ts b/src/main/webapp/app/account/register/register.component.spec.ts index 23ca1b0e..2e7f2ee7 100644 --- a/src/main/webapp/app/account/register/register.component.spec.ts +++ b/src/main/webapp/app/account/register/register.component.spec.ts @@ -7,7 +7,7 @@ import { TranslateModule, TranslateService } from '@ngx-translate/core'; import { EMAIL_ALREADY_USED_TYPE, LOGIN_ALREADY_USED_TYPE } from 'app/config/error.constants'; import { RegisterService } from './register.service'; -import { RegisterComponent } from './register.component'; +import RegisterComponent from './register.component'; describe('RegisterComponent', () => { let fixture: ComponentFixture; @@ -15,8 +15,7 @@ describe('RegisterComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ - imports: [TranslateModule.forRoot(), HttpClientTestingModule], - declarations: [RegisterComponent], + imports: [TranslateModule.forRoot(), HttpClientTestingModule, RegisterComponent], providers: [FormBuilder], }) .overrideTemplate(RegisterComponent, '') @@ -36,7 +35,7 @@ describe('RegisterComponent', () => { comp.register(); - expect(comp.doNotMatch).toBe(true); + expect(comp.doNotMatch()).toBe(true); }); it('should update success to true after creating an account', inject( @@ -58,22 +57,18 @@ describe('RegisterComponent', () => { login: '', langKey: 'en', }); - expect(comp.success).toBe(true); - expect(comp.errorUserExists).toBe(false); - expect(comp.errorEmailExists).toBe(false); - expect(comp.error).toBe(false); + expect(comp.success()).toBe(true); + expect(comp.errorUserExists()).toBe(false); + expect(comp.errorEmailExists()).toBe(false); + expect(comp.error()).toBe(false); }), )); it('should notify of user existence upon 400/login already in use', inject( [RegisterService], fakeAsync((service: RegisterService) => { - jest.spyOn(service, 'save').mockReturnValue( - throwError({ - status: 400, - error: { type: LOGIN_ALREADY_USED_TYPE }, - }), - ); + const err = { status: 400, error: { type: LOGIN_ALREADY_USED_TYPE } }; + jest.spyOn(service, 'save').mockReturnValue(throwError(() => err)); comp.registerForm.patchValue({ password: 'password', confirmPassword: 'password', @@ -82,21 +77,17 @@ describe('RegisterComponent', () => { comp.register(); tick(); - expect(comp.errorUserExists).toBe(true); - expect(comp.errorEmailExists).toBe(false); - expect(comp.error).toBe(false); + expect(comp.errorUserExists()).toBe(true); + expect(comp.errorEmailExists()).toBe(false); + expect(comp.error()).toBe(false); }), )); it('should notify of email existence upon 400/email address already in use', inject( [RegisterService], fakeAsync((service: RegisterService) => { - jest.spyOn(service, 'save').mockReturnValue( - throwError({ - status: 400, - error: { type: EMAIL_ALREADY_USED_TYPE }, - }), - ); + const err = { status: 400, error: { type: EMAIL_ALREADY_USED_TYPE } }; + jest.spyOn(service, 'save').mockReturnValue(throwError(() => err)); comp.registerForm.patchValue({ password: 'password', confirmPassword: 'password', @@ -105,20 +96,17 @@ describe('RegisterComponent', () => { comp.register(); tick(); - expect(comp.errorEmailExists).toBe(true); - expect(comp.errorUserExists).toBe(false); - expect(comp.error).toBe(false); + expect(comp.errorEmailExists()).toBe(true); + expect(comp.errorUserExists()).toBe(false); + expect(comp.error()).toBe(false); }), )); it('should notify of generic error', inject( [RegisterService], fakeAsync((service: RegisterService) => { - jest.spyOn(service, 'save').mockReturnValue( - throwError({ - status: 503, - }), - ); + const err = { status: 503 }; + jest.spyOn(service, 'save').mockReturnValue(throwError(() => err)); comp.registerForm.patchValue({ password: 'password', confirmPassword: 'password', @@ -127,9 +115,9 @@ describe('RegisterComponent', () => { comp.register(); tick(); - expect(comp.errorUserExists).toBe(false); - expect(comp.errorEmailExists).toBe(false); - expect(comp.error).toBe(true); + expect(comp.errorUserExists()).toBe(false); + expect(comp.errorEmailExists()).toBe(false); + expect(comp.error()).toBe(true); }), )); }); diff --git a/src/main/webapp/app/account/register/register.component.ts b/src/main/webapp/app/account/register/register.component.ts index 7d0b4d8e..ab6f316f 100644 --- a/src/main/webapp/app/account/register/register.component.ts +++ b/src/main/webapp/app/account/register/register.component.ts @@ -1,24 +1,29 @@ -import { Component, AfterViewInit, ElementRef, ViewChild } from '@angular/core'; +import { Component, AfterViewInit, ElementRef, inject, ViewChild, signal } from '@angular/core'; import { HttpErrorResponse } from '@angular/common/http'; -import { FormGroup, FormControl, Validators } from '@angular/forms'; +import { RouterModule } from '@angular/router'; +import { FormGroup, FormControl, Validators, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { TranslateService } from '@ngx-translate/core'; import { EMAIL_ALREADY_USED_TYPE, LOGIN_ALREADY_USED_TYPE } from 'app/config/error.constants'; +import SharedModule from 'app/shared/shared.module'; +import PasswordStrengthBarComponent from '../password/password-strength-bar/password-strength-bar.component'; import { RegisterService } from './register.service'; @Component({ + standalone: true, selector: 'jhi-register', + imports: [SharedModule, RouterModule, FormsModule, ReactiveFormsModule, PasswordStrengthBarComponent], templateUrl: './register.component.html', }) -export class RegisterComponent implements AfterViewInit { +export default class RegisterComponent implements AfterViewInit { @ViewChild('login', { static: false }) login?: ElementRef; - doNotMatch = false; - error = false; - errorEmailExists = false; - errorUserExists = false; - success = false; + doNotMatch = signal(false); + error = signal(false); + errorEmailExists = signal(false); + errorUserExists = signal(false); + success = signal(false); registerForm = new FormGroup({ login: new FormControl('', { @@ -44,10 +49,8 @@ export class RegisterComponent implements AfterViewInit { }), }); - constructor( - private translateService: TranslateService, - private registerService: RegisterService, - ) {} + private translateService = inject(TranslateService); + private registerService = inject(RegisterService); ngAfterViewInit(): void { if (this.login) { @@ -56,29 +59,29 @@ export class RegisterComponent implements AfterViewInit { } register(): void { - this.doNotMatch = false; - this.error = false; - this.errorEmailExists = false; - this.errorUserExists = false; + this.doNotMatch.set(false); + this.error.set(false); + this.errorEmailExists.set(false); + this.errorUserExists.set(false); const { password, confirmPassword } = this.registerForm.getRawValue(); if (password !== confirmPassword) { - this.doNotMatch = true; + this.doNotMatch.set(true); } else { const { login, email } = this.registerForm.getRawValue(); this.registerService .save({ login, email, password, langKey: this.translateService.currentLang }) - .subscribe({ next: () => (this.success = true), error: response => this.processError(response) }); + .subscribe({ next: () => this.success.set(true), error: response => this.processError(response) }); } } private processError(response: HttpErrorResponse): void { if (response.status === 400 && response.error.type === LOGIN_ALREADY_USED_TYPE) { - this.errorUserExists = true; + this.errorUserExists.set(true); } else if (response.status === 400 && response.error.type === EMAIL_ALREADY_USED_TYPE) { - this.errorEmailExists = true; + this.errorEmailExists.set(true); } else { - this.error = true; + this.error.set(true); } } } diff --git a/src/main/webapp/app/account/register/register.route.ts b/src/main/webapp/app/account/register/register.route.ts index 52525301..81323c3c 100644 --- a/src/main/webapp/app/account/register/register.route.ts +++ b/src/main/webapp/app/account/register/register.route.ts @@ -1,11 +1,11 @@ import { Route } from '@angular/router'; -import { RegisterComponent } from './register.component'; +import RegisterComponent from './register.component'; -export const registerRoute: Route = { +const registerRoute: Route = { path: 'register', component: RegisterComponent, - data: { - pageTitle: 'register.title', - }, + title: 'register.title', }; + +export default registerRoute; diff --git a/src/main/webapp/app/account/register/register.service.ts b/src/main/webapp/app/account/register/register.service.ts index 29c18330..8b6a6ff1 100644 --- a/src/main/webapp/app/account/register/register.service.ts +++ b/src/main/webapp/app/account/register/register.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { inject, Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; @@ -7,10 +7,8 @@ import { Registration } from './register.model'; @Injectable({ providedIn: 'root' }) export class RegisterService { - constructor( - private http: HttpClient, - private applicationConfigService: ApplicationConfigService, - ) {} + private http = inject(HttpClient); + private applicationConfigService = inject(ApplicationConfigService); save(registration: Registration): Observable<{}> { return this.http.post(this.applicationConfigService.getEndpointFor('api/register'), registration); diff --git a/src/main/webapp/app/account/settings/settings.component.html b/src/main/webapp/app/account/settings/settings.component.html index 4fb7967e..e30447f0 100644 --- a/src/main/webapp/app/account/settings/settings.component.html +++ b/src/main/webapp/app/account/settings/settings.component.html @@ -1,166 +1,154 @@
-

- User settings for [{{ settingsForm.value.login }}] -

+ @if (settingsForm.value.login) { +

+ User settings for [{{ settingsForm.value.login }}] +

+ } -
- Settings saved! -
+ @if (success()) { +
Settings saved!
+ } -
-
- - - -
+
+ + + + @if ( settingsForm.get('firstName')!.invalid && (settingsForm.get('firstName')!.dirty || settingsForm.get('firstName')!.touched) - " - > - - Your first name is required. - - - - Your first name is required to be at least 1 character - - - - Your first name cannot be longer than 50 characters - + ) { +
+ @if (settingsForm.get('firstName')?.errors?.required) { + Your first name is required. + } + + @if (settingsForm.get('firstName')?.errors?.minlength) { + Your first name is required to be at least 1 character + } + + @if (settingsForm.get('firstName')?.errors?.maxlength) { + Your first name cannot be longer than 50 characters + } +
+ }
-
- -
- - - -
- - Your last name is required. - - - - Your last name is required to be at least 1 character - - - - Your last name cannot be longer than 50 characters - + +
+ + + + @if (settingsForm.get('lastName')!.invalid && (settingsForm.get('lastName')!.dirty || settingsForm.get('lastName')!.touched)) { +
+ @if (settingsForm.get('lastName')?.errors?.required) { + Your last name is required. + } + + @if (settingsForm.get('lastName')?.errors?.minlength) { + Your last name is required to be at least 1 character + } + + @if (settingsForm.get('lastName')?.errors?.maxlength) { + Your last name cannot be longer than 50 characters + } +
+ }
-
- -
- - - -
- - Your email is required. - - - - Your email is invalid. - - - - Your email is required to be at least 5 characters. - - - - Your email cannot be longer than 50 characters. - + +
+ + + + @if (settingsForm.get('email')!.invalid && (settingsForm.get('email')!.dirty || settingsForm.get('email')!.touched)) { +
+ @if (settingsForm.get('email')?.errors?.required) { + Your email is required. + } + + @if (settingsForm.get('email')?.errors?.email) { + Your email is invalid. + } + + @if (settingsForm.get('email')?.errors?.minlength) { + Your email is required to be at least 5 characters. + } + + @if (settingsForm.get('email')?.errors?.maxlength) { + Your email cannot be longer than 50 characters. + } +
+ }
-
- -
- - -
- - - + + @if (languages && languages.length > 0) { +
+ + +
+ } + + + + }
diff --git a/src/main/webapp/app/account/settings/settings.component.spec.ts b/src/main/webapp/app/account/settings/settings.component.spec.ts index 0b540754..9f7c56a7 100644 --- a/src/main/webapp/app/account/settings/settings.component.spec.ts +++ b/src/main/webapp/app/account/settings/settings.component.spec.ts @@ -9,7 +9,7 @@ import { TranslateModule } from '@ngx-translate/core'; import { AccountService } from 'app/core/auth/account.service'; import { Account } from 'app/core/auth/account.model'; -import { SettingsComponent } from './settings.component'; +import SettingsComponent from './settings.component'; describe('SettingsComponent', () => { let comp: SettingsComponent; @@ -28,8 +28,7 @@ describe('SettingsComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ - imports: [TranslateModule.forRoot(), HttpClientTestingModule], - declarations: [SettingsComponent], + imports: [TranslateModule.forRoot(), HttpClientTestingModule, SettingsComponent], providers: [FormBuilder, AccountService], }) .overrideTemplate(SettingsComponent, '') @@ -74,7 +73,7 @@ describe('SettingsComponent', () => { comp.save(); // THEN - expect(comp.success).toBe(true); + expect(comp.success()).toBe(true); }); it('should notify of error upon failed save', () => { @@ -86,6 +85,6 @@ describe('SettingsComponent', () => { comp.save(); // THEN - expect(comp.success).toBe(false); + expect(comp.success()).toBe(false); }); }); diff --git a/src/main/webapp/app/account/settings/settings.component.ts b/src/main/webapp/app/account/settings/settings.component.ts index efb2d255..0f15ec2f 100644 --- a/src/main/webapp/app/account/settings/settings.component.ts +++ b/src/main/webapp/app/account/settings/settings.component.ts @@ -1,7 +1,8 @@ -import { Component, OnInit } from '@angular/core'; -import { FormGroup, FormControl, Validators } from '@angular/forms'; +import { Component, inject, OnInit, signal } from '@angular/core'; +import { FormGroup, FormControl, Validators, FormsModule, ReactiveFormsModule } from '@angular/forms'; import { TranslateService } from '@ngx-translate/core'; +import SharedModule from 'app/shared/shared.module'; import { AccountService } from 'app/core/auth/account.service'; import { Account } from 'app/core/auth/account.model'; import { LANGUAGES } from 'app/config/language.constants'; @@ -9,11 +10,13 @@ import { LANGUAGES } from 'app/config/language.constants'; const initialAccount: Account = {} as Account; @Component({ + standalone: true, selector: 'jhi-settings', + imports: [SharedModule, FormsModule, ReactiveFormsModule], templateUrl: './settings.component.html', }) -export class SettingsComponent implements OnInit { - success = false; +export default class SettingsComponent implements OnInit { + success = signal(false); languages = LANGUAGES; settingsForm = new FormGroup({ @@ -37,10 +40,8 @@ export class SettingsComponent implements OnInit { login: new FormControl(initialAccount.login, { nonNullable: true }), }); - constructor( - private accountService: AccountService, - private translateService: TranslateService, - ) {} + private accountService = inject(AccountService); + private translateService = inject(TranslateService); ngOnInit(): void { this.accountService.identity().subscribe(account => { @@ -51,11 +52,11 @@ export class SettingsComponent implements OnInit { } save(): void { - this.success = false; + this.success.set(false); const account = this.settingsForm.getRawValue(); this.accountService.save(account).subscribe(() => { - this.success = true; + this.success.set(true); this.accountService.authenticate(account); diff --git a/src/main/webapp/app/account/settings/settings.route.ts b/src/main/webapp/app/account/settings/settings.route.ts index c940aece..3c930661 100644 --- a/src/main/webapp/app/account/settings/settings.route.ts +++ b/src/main/webapp/app/account/settings/settings.route.ts @@ -1,13 +1,13 @@ import { Route } from '@angular/router'; import { UserRouteAccessService } from 'app/core/auth/user-route-access.service'; -import { SettingsComponent } from './settings.component'; +import SettingsComponent from './settings.component'; -export const settingsRoute: Route = { +const settingsRoute: Route = { path: 'settings', component: SettingsComponent, - data: { - pageTitle: 'global.menu.account.settings', - }, + title: 'global.menu.account.settings', canActivate: [UserRouteAccessService], }; + +export default settingsRoute; diff --git a/src/main/webapp/app/admin/admin-routing.module.ts b/src/main/webapp/app/admin/admin-routing.module.ts deleted file mode 100644 index 6eec19da..00000000 --- a/src/main/webapp/app/admin/admin-routing.module.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { NgModule } from '@angular/core'; -import { RouterModule } from '@angular/router'; -import { TwentyOnePointsElasticsearchReindexModule } from './elasticsearch-reindex/elasticsearch-reindex.module'; -/* jhipster-needle-add-admin-module-import - JHipster will add admin modules imports here */ - -@NgModule({ - imports: [ - TwentyOnePointsElasticsearchReindexModule, - /* jhipster-needle-add-admin-module - JHipster will add admin modules here */ - RouterModule.forChild([ - { - path: 'user-management', - loadChildren: () => import('./user-management/user-management.module').then(m => m.UserManagementModule), - data: { - pageTitle: 'userManagement.home.title', - }, - }, - { - path: 'docs', - loadChildren: () => import('./docs/docs.module').then(m => m.DocsModule), - }, - { - path: 'configuration', - loadChildren: () => import('./configuration/configuration.module').then(m => m.ConfigurationModule), - }, - { - path: 'health', - loadChildren: () => import('./health/health.module').then(m => m.HealthModule), - }, - { - path: 'logs', - loadChildren: () => import('./logs/logs.module').then(m => m.LogsModule), - }, - { - path: 'metrics', - loadChildren: () => import('./metrics/metrics.module').then(m => m.MetricsModule), - }, - { - path: 'elasticsearch-reindex', - loadChildren: () => - import('./elasticsearch-reindex/elasticsearch-reindex.module').then(m => m.TwentyOnePointsElasticsearchReindexModule), - }, - /* jhipster-needle-add-admin-route - JHipster will add admin routes here */ - ]), - ], -}) -export class AdminRoutingModule {} diff --git a/src/main/webapp/app/admin/admin.routes.ts b/src/main/webapp/app/admin/admin.routes.ts new file mode 100644 index 00000000..cfb8bc20 --- /dev/null +++ b/src/main/webapp/app/admin/admin.routes.ts @@ -0,0 +1,38 @@ +import { Routes } from '@angular/router'; +/* jhipster-needle-add-admin-module-import - JHipster will add admin modules imports here */ + +const routes: Routes = [ + { + path: 'user-management', + loadChildren: () => import('./user-management/user-management.route'), + title: 'userManagement.home.title', + }, + { + path: 'docs', + loadComponent: () => import('./docs/docs.component'), + title: 'global.menu.admin.apidocs', + }, + { + path: 'configuration', + loadComponent: () => import('./configuration/configuration.component'), + title: 'configuration.title', + }, + { + path: 'health', + loadComponent: () => import('./health/health.component'), + title: 'health.title', + }, + { + path: 'logs', + loadComponent: () => import('./logs/logs.component'), + title: 'logs.title', + }, + { + path: 'metrics', + loadComponent: () => import('./metrics/metrics.component'), + title: 'metrics.title', + }, + /* jhipster-needle-add-admin-route - JHipster will add admin routes here */ +]; + +export default routes; diff --git a/src/main/webapp/app/admin/configuration/configuration.component.html b/src/main/webapp/app/admin/configuration/configuration.component.html index e76d02c3..180517e5 100644 --- a/src/main/webapp/app/admin/configuration/configuration.component.html +++ b/src/main/webapp/app/admin/configuration/configuration.component.html @@ -1,57 +1,70 @@ -
-

Configuration

+@if (allBeans()) { +
+

Configuration

- Filter (by prefix) - + Filter (by prefix) + -

Spring configuration

+

Spring configuration

- - - - - - - - - - - - - -
- Prefix - Properties
- {{ bean.prefix }} - -
-
{{ property.key }}
-
- {{ property.value | json }} -
-
-
- -
-

- {{ propertySource.name }} -

- - +
- - - + + + - - - - + @for (bean of beans(); track $index) { + + + + + }
PropertyValue
+ Prefix + Properties
{{ property.key }} - {{ property.value.value }} -
+ {{ bean.prefix }} + + @for (property of bean.properties | keyvalue; track property.key) { +
+
{{ property.key }}
+
+ {{ property.value | json }} +
+
+ } +
+ + @for (propertySource of propertySources(); track i; let i = $index) { +
+

+ {{ propertySource.name }} +

+ + + + + + + + + + @for (property of propertySource.properties | keyvalue; track property.key) { + + + + + } + +
PropertyValue
{{ property.key }} + {{ property.value.value }} +
+
+ }
-
+} diff --git a/src/main/webapp/app/admin/configuration/configuration.component.spec.ts b/src/main/webapp/app/admin/configuration/configuration.component.spec.ts index 12df2b28..be39e5ff 100644 --- a/src/main/webapp/app/admin/configuration/configuration.component.spec.ts +++ b/src/main/webapp/app/admin/configuration/configuration.component.spec.ts @@ -2,7 +2,7 @@ import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { of } from 'rxjs'; -import { ConfigurationComponent } from './configuration.component'; +import ConfigurationComponent from './configuration.component'; import { ConfigurationService } from './configuration.service'; import { Bean, PropertySource } from './configuration.model'; @@ -13,8 +13,7 @@ describe('ConfigurationComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ - imports: [HttpClientTestingModule], - declarations: [ConfigurationComponent], + imports: [HttpClientTestingModule, ConfigurationComponent], providers: [ConfigurationService], }) .overrideTemplate(ConfigurationComponent, '') @@ -59,9 +58,9 @@ describe('ConfigurationComponent', () => { // THEN expect(service.getBeans).toHaveBeenCalled(); expect(service.getPropertySources).toHaveBeenCalled(); - expect(comp.allBeans).toEqual(beans); - expect(comp.beans).toEqual(beans); - expect(comp.propertySources).toEqual(propertySources); + expect(comp.allBeans()).toEqual(beans); + expect(comp.beans()).toEqual(beans); + expect(comp.propertySources()).toEqual(propertySources); }); }); }); diff --git a/src/main/webapp/app/admin/configuration/configuration.component.ts b/src/main/webapp/app/admin/configuration/configuration.component.ts index 939d1aec..ba5949ba 100644 --- a/src/main/webapp/app/admin/configuration/configuration.component.ts +++ b/src/main/webapp/app/admin/configuration/configuration.component.ts @@ -1,35 +1,44 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, computed, inject, OnInit, signal } from '@angular/core'; +import SharedModule from 'app/shared/shared.module'; +import { FormsModule } from '@angular/forms'; +import { SortDirective, SortByDirective, sortStateSignal, SortService } from 'app/shared/sort'; import { ConfigurationService } from './configuration.service'; import { Bean, PropertySource } from './configuration.model'; @Component({ + standalone: true, selector: 'jhi-configuration', templateUrl: './configuration.component.html', + imports: [SharedModule, FormsModule, SortDirective, SortByDirective], }) -export class ConfigurationComponent implements OnInit { - allBeans!: Bean[]; - beans: Bean[] = []; - beansFilter = ''; - beansAscending = true; - propertySources: PropertySource[] = []; +export default class ConfigurationComponent implements OnInit { + allBeans = signal(undefined); + beansFilter = signal(''); + propertySources = signal([]); + sortState = sortStateSignal({ predicate: 'prefix', order: 'asc' }); + beans = computed(() => { + let data = this.allBeans() ?? []; + const beansFilter = this.beansFilter(); + if (beansFilter) { + data = data.filter(bean => bean.prefix.toLowerCase().includes(beansFilter.toLowerCase())); + } - constructor(private configurationService: ConfigurationService) {} + const { order, predicate } = this.sortState(); + if (predicate && order) { + data = data.sort(this.sortService.startSort({ predicate, order })); + } + return data; + }); + + private sortService = inject(SortService); + private configurationService = inject(ConfigurationService); ngOnInit(): void { this.configurationService.getBeans().subscribe(beans => { - this.allBeans = beans; - this.filterAndSortBeans(); + this.allBeans.set(beans); }); - this.configurationService.getPropertySources().subscribe(propertySources => (this.propertySources = propertySources)); - } - - filterAndSortBeans(): void { - const beansAscendingValue = this.beansAscending ? -1 : 1; - const beansAscendingValueReverse = this.beansAscending ? 1 : -1; - this.beans = this.allBeans - .filter(bean => !this.beansFilter || bean.prefix.toLowerCase().includes(this.beansFilter.toLowerCase())) - .sort((a, b) => (a.prefix < b.prefix ? beansAscendingValue : beansAscendingValueReverse)); + this.configurationService.getPropertySources().subscribe(propertySources => this.propertySources.set(propertySources)); } } diff --git a/src/main/webapp/app/admin/configuration/configuration.module.ts b/src/main/webapp/app/admin/configuration/configuration.module.ts deleted file mode 100644 index 43ade13c..00000000 --- a/src/main/webapp/app/admin/configuration/configuration.module.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { NgModule } from '@angular/core'; -import { RouterModule } from '@angular/router'; -import { SharedModule } from 'app/shared/shared.module'; - -import { ConfigurationComponent } from './configuration.component'; -import { configurationRoute } from './configuration.route'; - -@NgModule({ - imports: [SharedModule, RouterModule.forChild([configurationRoute])], - declarations: [ConfigurationComponent], -}) -export class ConfigurationModule {} diff --git a/src/main/webapp/app/admin/configuration/configuration.route.ts b/src/main/webapp/app/admin/configuration/configuration.route.ts deleted file mode 100644 index 119e4335..00000000 --- a/src/main/webapp/app/admin/configuration/configuration.route.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Route } from '@angular/router'; - -import { ConfigurationComponent } from './configuration.component'; - -export const configurationRoute: Route = { - path: '', - component: ConfigurationComponent, - data: { - pageTitle: 'configuration.title', - }, -}; diff --git a/src/main/webapp/app/admin/configuration/configuration.service.ts b/src/main/webapp/app/admin/configuration/configuration.service.ts index f03a0cce..eecb1c6f 100644 --- a/src/main/webapp/app/admin/configuration/configuration.service.ts +++ b/src/main/webapp/app/admin/configuration/configuration.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { inject, Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; @@ -8,10 +8,8 @@ import { Bean, Beans, ConfigProps, Env, PropertySource } from './configuration.m @Injectable({ providedIn: 'root' }) export class ConfigurationService { - constructor( - private http: HttpClient, - private applicationConfigService: ApplicationConfigService, - ) {} + private http = inject(HttpClient); + private applicationConfigService = inject(ApplicationConfigService); getBeans(): Observable { return this.http.get(this.applicationConfigService.getEndpointFor('management/configprops')).pipe( @@ -19,7 +17,7 @@ export class ConfigurationService { Object.values( Object.values(configProps.contexts) .map(context => context.beans) - .reduce((allBeans: Beans, contextBeans: Beans) => ({ ...allBeans, ...contextBeans })), + .reduce((allBeans: Beans, contextBeans: Beans) => ({ ...allBeans, ...contextBeans }), {}), ), ), ); diff --git a/src/main/webapp/app/admin/docs/docs.component.scss b/src/main/webapp/app/admin/docs/docs.component.scss index 541c3d79..bb9a6cc8 100644 --- a/src/main/webapp/app/admin/docs/docs.component.scss +++ b/src/main/webapp/app/admin/docs/docs.component.scss @@ -1,5 +1,5 @@ -@import '~bootstrap/scss/functions'; -@import '~bootstrap/scss/variables'; +@import 'bootstrap/scss/functions'; +@import 'bootstrap/scss/variables'; iframe { background: white; diff --git a/src/main/webapp/app/admin/docs/docs.component.ts b/src/main/webapp/app/admin/docs/docs.component.ts index 53b4b14c..150ea5cd 100644 --- a/src/main/webapp/app/admin/docs/docs.component.ts +++ b/src/main/webapp/app/admin/docs/docs.component.ts @@ -1,8 +1,9 @@ import { Component } from '@angular/core'; @Component({ + standalone: true, selector: 'jhi-docs', templateUrl: './docs.component.html', - styleUrls: ['./docs.component.scss'], + styleUrl: './docs.component.scss', }) -export class DocsComponent {} +export default class DocsComponent {} diff --git a/src/main/webapp/app/admin/docs/docs.module.ts b/src/main/webapp/app/admin/docs/docs.module.ts deleted file mode 100644 index 7b9bbb66..00000000 --- a/src/main/webapp/app/admin/docs/docs.module.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { NgModule } from '@angular/core'; -import { RouterModule } from '@angular/router'; -import { SharedModule } from 'app/shared/shared.module'; - -import { DocsComponent } from './docs.component'; -import { docsRoute } from './docs.route'; - -@NgModule({ - imports: [SharedModule, RouterModule.forChild([docsRoute])], - declarations: [DocsComponent], -}) -export class DocsModule {} diff --git a/src/main/webapp/app/admin/docs/docs.route.ts b/src/main/webapp/app/admin/docs/docs.route.ts deleted file mode 100644 index e51770df..00000000 --- a/src/main/webapp/app/admin/docs/docs.route.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Route } from '@angular/router'; - -import { DocsComponent } from './docs.component'; - -export const docsRoute: Route = { - path: '', - component: DocsComponent, - data: { - pageTitle: 'global.menu.admin.apidocs', - }, -}; diff --git a/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex-modal.component.html b/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex-modal.component.html deleted file mode 100644 index 220e35eb..00000000 --- a/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex-modal.component.html +++ /dev/null @@ -1,18 +0,0 @@ -
- - - -
diff --git a/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex-modal.component.ts b/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex-modal.component.ts deleted file mode 100644 index 8ed3d6fe..00000000 --- a/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex-modal.component.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Component } from '@angular/core'; -import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; - -import { ElasticsearchReindexService } from './elasticsearch-reindex.service'; - -@Component({ - selector: 'jhi-elasticsearch-reindex-modal', - templateUrl: './elasticsearch-reindex-modal.component.html', -}) -export class ElasticsearchReindexModalComponent { - constructor( - private elasticsearchReindexService: ElasticsearchReindexService, - public activeModal: NgbActiveModal, - ) {} - - reindex(): void { - this.elasticsearchReindexService.reindex().subscribe(() => this.activeModal.dismiss()); - } -} diff --git a/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex-selected-modal.component.ts b/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex-selected-modal.component.ts deleted file mode 100644 index 200a39cb..00000000 --- a/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex-selected-modal.component.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Component } from '@angular/core'; -import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; - -import { ElasticsearchReindexService } from './elasticsearch-reindex.service'; - -@Component({ - selector: 'jhi-elasticsearch-reindex-modal', - templateUrl: './elasticsearch-reindex-modal.component.html', -}) -export class ElasticsearchReindexSelectedModalComponent { - entities: string[]; - constructor( - private elasticsearchReindexService: ElasticsearchReindexService, - public activeModal: NgbActiveModal, - ) { - this.entities = []; - } - - reindex(): void { - this.elasticsearchReindexService.reindexSelected(this.entities).subscribe(() => this.activeModal.dismiss()); - } -} diff --git a/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex.component.html b/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex.component.html deleted file mode 100644 index 57608da0..00000000 --- a/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex.component.html +++ /dev/null @@ -1,31 +0,0 @@ -
-

- Reindex Elasticsearch -

- -

- This will send a request to reindex all data from your primary datasource into Elasticsearch. This will fix missing data and mapping - issues, however you will lose any ES data and mapping that was defined outside of your application. This request will return immediately - and the reindexing will run in the background. You will see "Elasticsearch: Successfully performed reindexing" in your logs when this is - complete. -

-

- This can take a long time and is susceptible to concurrency issues so it should be performed when the number of users is low. -

- - -
- -
-
-
- -
diff --git a/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex.component.ts b/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex.component.ts deleted file mode 100644 index 1e64bc2a..00000000 --- a/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex.component.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { Component } from '@angular/core'; -import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; - -import { ElasticsearchReindexModalComponent } from './elasticsearch-reindex-modal.component'; -import { ElasticsearchReindexSelectedModalComponent } from './elasticsearch-reindex-selected-modal.component'; - -@Component({ - selector: 'jhi-elasticsearch-reindex', - templateUrl: './elasticsearch-reindex.component.html', -}) -export class ElasticsearchReindexComponent { - entities: string[]; - reindexType: string; - checks: { [key: string]: boolean } = {}; - - constructor(private modalService: NgbModal) { - this.reindexType = 'all'; - this.entities = ['Points', 'Weight', 'BloodPressure', 'Preferences', 'User']; - } - - doReindex(): void { - if (this.reindexType === 'all') { - this.showConfirm(); - } else { - this.showConfirmSelected(); - } - } - - showConfirm(): void { - this.modalService.open(ElasticsearchReindexModalComponent); - } - - showConfirmSelected(): void { - const activeModal = this.modalService.open(ElasticsearchReindexSelectedModalComponent); - const checks = this.checks; - const reindexList = this.entities.filter(function (name): any { - return checks[name]; - }); - activeModal.componentInstance.entities = reindexList; - } -} diff --git a/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex.module.ts b/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex.module.ts deleted file mode 100644 index fbb945cf..00000000 --- a/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex.module.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; -import { RouterModule } from '@angular/router'; - -import { SharedModule } from 'app/shared/shared.module'; - -import { - ElasticsearchReindexComponent, - ElasticsearchReindexModalComponent, - ElasticsearchReindexSelectedModalComponent, - ElasticsearchReindexService, - elasticsearchReindexRoute, -} from './'; - -const ADMIN_ROUTES = [elasticsearchReindexRoute]; - -@NgModule({ - imports: [SharedModule, RouterModule.forChild(ADMIN_ROUTES)], - declarations: [ElasticsearchReindexComponent, ElasticsearchReindexModalComponent, ElasticsearchReindexSelectedModalComponent], - entryComponents: [ElasticsearchReindexModalComponent, ElasticsearchReindexSelectedModalComponent], - providers: [ElasticsearchReindexService], - schemas: [CUSTOM_ELEMENTS_SCHEMA], -}) -export class TwentyOnePointsElasticsearchReindexModule {} diff --git a/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex.route.ts b/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex.route.ts deleted file mode 100644 index 04924b9c..00000000 --- a/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex.route.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Route } from '@angular/router'; -import { UserRouteAccessService } from 'app/core/auth/user-route-access.service'; -import { ElasticsearchReindexComponent } from './elasticsearch-reindex.component'; - -export const elasticsearchReindexRoute: Route = { - path: '', - component: ElasticsearchReindexComponent, - data: { - authorities: ['ROLE_ADMIN'], - pageTitle: 'elasticsearch.reindex.title', - }, - canActivate: [UserRouteAccessService], -}; diff --git a/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex.service.ts b/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex.service.ts deleted file mode 100644 index 26fc1c65..00000000 --- a/src/main/webapp/app/admin/elasticsearch-reindex/elasticsearch-reindex.service.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Injectable } from '@angular/core'; -import { HttpClient, HttpResponse } from '@angular/common/http'; -import { Observable } from 'rxjs'; - -@Injectable({ providedIn: 'root' }) -export class ElasticsearchReindexService { - constructor(private http: HttpClient) {} - - reindex(): Observable> { - return this.http.post>('api/elasticsearch/index', { observe: 'response' }); - } - - reindexSelected(selectedEntities: string[]): Observable> { - return this.http.post>('api/elasticsearch/selected', selectedEntities, { observe: 'response' }); - } -} diff --git a/src/main/webapp/app/admin/elasticsearch-reindex/index.ts b/src/main/webapp/app/admin/elasticsearch-reindex/index.ts deleted file mode 100644 index d90a6576..00000000 --- a/src/main/webapp/app/admin/elasticsearch-reindex/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from './elasticsearch-reindex.component'; -export * from './elasticsearch-reindex-modal.component'; -export * from './elasticsearch-reindex-selected-modal.component'; -export * from './elasticsearch-reindex.service'; -export * from './elasticsearch-reindex.route'; diff --git a/src/main/webapp/app/admin/health/health.component.html b/src/main/webapp/app/admin/health/health.component.html index 08e62ac3..8987cb9d 100644 --- a/src/main/webapp/app/admin/health/health.component.html +++ b/src/main/webapp/app/admin/health/health.component.html @@ -1,6 +1,6 @@

- Health Checks + Health Checks

diff --git a/src/main/webapp/app/admin/health/health.component.spec.ts b/src/main/webapp/app/admin/health/health.component.spec.ts index 3cfdf9f8..97ce8aba 100644 --- a/src/main/webapp/app/admin/health/health.component.spec.ts +++ b/src/main/webapp/app/admin/health/health.component.spec.ts @@ -3,7 +3,7 @@ import { HttpErrorResponse } from '@angular/common/http'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { of, throwError } from 'rxjs'; -import { HealthComponent } from './health.component'; +import HealthComponent from './health.component'; import { HealthService } from './health.service'; import { Health } from './health.model'; @@ -14,8 +14,7 @@ describe('HealthComponent', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ - imports: [HttpClientTestingModule], - declarations: [HealthComponent], + imports: [HttpClientTestingModule, HealthComponent], }) .overrideTemplate(HealthComponent, '') .compileComponents(); diff --git a/src/main/webapp/app/admin/health/health.component.ts b/src/main/webapp/app/admin/health/health.component.ts index c384310d..392b7506 100644 --- a/src/main/webapp/app/admin/health/health.component.ts +++ b/src/main/webapp/app/admin/health/health.component.ts @@ -1,22 +1,23 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, inject, OnInit } from '@angular/core'; import { HttpErrorResponse } from '@angular/common/http'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import SharedModule from 'app/shared/shared.module'; import { HealthService } from './health.service'; import { Health, HealthDetails, HealthStatus } from './health.model'; -import { HealthModalComponent } from './modal/health-modal.component'; +import HealthModalComponent from './modal/health-modal.component'; @Component({ + standalone: true, selector: 'jhi-health', templateUrl: './health.component.html', + imports: [SharedModule, HealthModalComponent], }) -export class HealthComponent implements OnInit { +export default class HealthComponent implements OnInit { health?: Health; - constructor( - private modalService: NgbModal, - private healthService: HealthService, - ) {} + private modalService = inject(NgbModal); + private healthService = inject(HealthService); ngOnInit(): void { this.refresh(); diff --git a/src/main/webapp/app/admin/health/health.module.ts b/src/main/webapp/app/admin/health/health.module.ts deleted file mode 100644 index 554d1b8e..00000000 --- a/src/main/webapp/app/admin/health/health.module.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { NgModule } from '@angular/core'; -import { RouterModule } from '@angular/router'; -import { SharedModule } from 'app/shared/shared.module'; - -import { HealthComponent } from './health.component'; -import { HealthModalComponent } from './modal/health-modal.component'; -import { healthRoute } from './health.route'; - -@NgModule({ - imports: [SharedModule, RouterModule.forChild([healthRoute])], - declarations: [HealthComponent, HealthModalComponent], -}) -export class HealthModule {} diff --git a/src/main/webapp/app/admin/health/health.route.ts b/src/main/webapp/app/admin/health/health.route.ts deleted file mode 100644 index bd760272..00000000 --- a/src/main/webapp/app/admin/health/health.route.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Route } from '@angular/router'; - -import { HealthComponent } from './health.component'; - -export const healthRoute: Route = { - path: '', - component: HealthComponent, - data: { - pageTitle: 'health.title', - }, -}; diff --git a/src/main/webapp/app/admin/health/health.service.ts b/src/main/webapp/app/admin/health/health.service.ts index 7a2d420d..c62fd83d 100644 --- a/src/main/webapp/app/admin/health/health.service.ts +++ b/src/main/webapp/app/admin/health/health.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@angular/core'; +import { inject, Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Observable } from 'rxjs'; @@ -7,10 +7,8 @@ import { Health } from './health.model'; @Injectable({ providedIn: 'root' }) export class HealthService { - constructor( - private http: HttpClient, - private applicationConfigService: ApplicationConfigService, - ) {} + private http = inject(HttpClient); + private applicationConfigService = inject(ApplicationConfigService); checkHealth(): Observable { return this.http.get(this.applicationConfigService.getEndpointFor('management/health')); diff --git a/src/main/webapp/app/admin/health/modal/health-modal.component.html b/src/main/webapp/app/admin/health/modal/health-modal.component.html index f55b8765..0accf8b1 100644 --- a/src/main/webapp/app/admin/health/modal/health-modal.component.html +++ b/src/main/webapp/app/admin/health/modal/health-modal.component.html @@ -1,34 +1,50 @@ -
- No Preferences found -
+ @if (preferences?.length === 0) { +
+ No Preferences found +
+ } + + @if (preferences && preferences.length > 0) { +
+ + + + + } + +
+
+ ID -
- - - - - - - - - - - - - - - - - + + + + + + + @for (preferences of preferences; track trackId) { + + + + + + - - -
-
- ID - -
-
-
- Weekly Goal - -
-
-
- Weight Units - -
-
-
- User - -
-
- {{ preferences.id }} - {{ preferences.weeklyGoal }}{{ preferences.weightUnits }} - {{ preferences.user?.login }} - -
- + +
+ +
+
+ Weekly Goal - + +
+
+
+ Weight Units + @if (!currentSearch) { + + } +
+
+
+ User + +
+
+ {{ preferences.id }} + {{ preferences.weeklyGoal }} + {{ { null: '', KG: 'KG', LB: 'LB' }[preferences.weightUnits ?? 'null'] }} + + {{ preferences.user?.login }} + +
+ + + View + - -
-
-
+ + + Edit + + + +
+ +
+
+ }
diff --git a/src/main/webapp/app/entities/preferences/list/preferences.component.spec.ts b/src/main/webapp/app/entities/preferences/list/preferences.component.spec.ts index d19200d5..b9ac3008 100644 --- a/src/main/webapp/app/entities/preferences/list/preferences.component.spec.ts +++ b/src/main/webapp/app/entities/preferences/list/preferences.component.spec.ts @@ -1,23 +1,25 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, fakeAsync, inject, tick } from '@angular/core/testing'; import { HttpHeaders, HttpResponse } from '@angular/common/http'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { ActivatedRoute } from '@angular/router'; -import { RouterTestingModule } from '@angular/router/testing'; -import { of } from 'rxjs'; +import { of, Subject } from 'rxjs'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { sampleWithRequiredData } from '../preferences.test-samples'; import { PreferencesService } from '../service/preferences.service'; import { PreferencesComponent } from './preferences.component'; +import SpyInstance = jest.SpyInstance; describe('Preferences Management Component', () => { let comp: PreferencesComponent; let fixture: ComponentFixture; let service: PreferencesService; + let routerNavigateSpy: SpyInstance>; beforeEach(() => { TestBed.configureTestingModule({ - imports: [RouterTestingModule.withRoutes([{ path: 'preferences', component: PreferencesComponent }]), HttpClientTestingModule], - declarations: [PreferencesComponent], + imports: [HttpClientTestingModule, PreferencesComponent], providers: [ { provide: ActivatedRoute, @@ -32,7 +34,14 @@ describe('Preferences Management Component', () => { sort: 'id,desc', }), ), - snapshot: { queryParams: {} }, + snapshot: { + queryParams: {}, + queryParamMap: jest.requireActual('@angular/router').convertToParamMap({ + page: '1', + size: '1', + sort: 'id,desc', + }), + }, }, }, ], @@ -43,16 +52,30 @@ describe('Preferences Management Component', () => { fixture = TestBed.createComponent(PreferencesComponent); comp = fixture.componentInstance; service = TestBed.inject(PreferencesService); + routerNavigateSpy = jest.spyOn(comp.router, 'navigate'); - const headers = new HttpHeaders(); - jest.spyOn(service, 'query').mockReturnValue( - of( - new HttpResponse({ - body: [{ id: 123 }], - headers, - }), - ), - ); + jest + .spyOn(service, 'query') + .mockReturnValueOnce( + of( + new HttpResponse({ + body: [{ id: 123 }], + headers: new HttpHeaders({ + link: '; rel="next"', + }), + }), + ), + ) + .mockReturnValueOnce( + of( + new HttpResponse({ + body: [{ id: 456 }], + headers: new HttpHeaders({ + link: '; rel="prev",; rel="next"', + }), + }), + ), + ); }); it('Should call load all on init', () => { @@ -73,4 +96,74 @@ describe('Preferences Management Component', () => { expect(id).toBe(entity.id); }); }); + + it('should calculate the sort attribute for a non-id attribute', () => { + // WHEN + comp.navigateToWithComponentValues({ predicate: 'non-existing-column', order: 'asc' }); + + // THEN + expect(routerNavigateSpy).toHaveBeenLastCalledWith( + expect.anything(), + expect.objectContaining({ + queryParams: expect.objectContaining({ + sort: ['non-existing-column,asc'], + }), + }), + ); + }); + + it('should calculate the sort attribute for an id', () => { + // WHEN + comp.ngOnInit(); + + // THEN + expect(service.query).toHaveBeenLastCalledWith(expect.objectContaining({ sort: ['id,desc'] })); + }); + + describe('delete', () => { + let ngbModal: NgbModal; + let deleteModalMock: any; + + beforeEach(() => { + deleteModalMock = { componentInstance: {}, closed: new Subject() }; + // NgbModal is not a singleton using TestBed.inject. + // ngbModal = TestBed.inject(NgbModal); + ngbModal = (comp as any).modalService; + jest.spyOn(ngbModal, 'open').mockReturnValue(deleteModalMock); + }); + + it('on confirm should call load', inject( + [], + fakeAsync(() => { + // GIVEN + jest.spyOn(comp, 'load'); + + // WHEN + comp.delete(sampleWithRequiredData); + deleteModalMock.closed.next('deleted'); + tick(); + + // THEN + expect(ngbModal.open).toHaveBeenCalled(); + expect(comp.load).toHaveBeenCalled(); + }), + )); + + it('on dismiss should call load', inject( + [], + fakeAsync(() => { + // GIVEN + jest.spyOn(comp, 'load'); + + // WHEN + comp.delete(sampleWithRequiredData); + deleteModalMock.closed.next(); + tick(); + + // THEN + expect(ngbModal.open).toHaveBeenCalled(); + expect(comp.load).not.toHaveBeenCalled(); + }), + )); + }); }); diff --git a/src/main/webapp/app/entities/preferences/list/preferences.component.ts b/src/main/webapp/app/entities/preferences/list/preferences.component.ts index 0a8cb28e..a56c8a60 100644 --- a/src/main/webapp/app/entities/preferences/list/preferences.component.ts +++ b/src/main/webapp/app/entities/preferences/list/preferences.component.ts @@ -1,49 +1,71 @@ -import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute, Data, ParamMap, Router } from '@angular/router'; -import { combineLatest, filter, Observable, switchMap, tap } from 'rxjs'; +import { Component, NgZone, inject, OnInit } from '@angular/core'; +import { ActivatedRoute, Data, ParamMap, Router, RouterModule } from '@angular/router'; +import { combineLatest, filter, Observable, Subscription, tap } from 'rxjs'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; -import { ASC, DESC, SORT, ITEM_DELETED_EVENT, DEFAULT_SORT_DATA } from 'app/config/navigation.constants'; -import { SortService } from 'app/shared/sort/sort.service'; +import SharedModule from 'app/shared/shared.module'; +import { sortStateSignal, SortDirective, SortByDirective, type SortState, SortService } from 'app/shared/sort'; +import { DurationPipe, FormatMediumDatetimePipe, FormatMediumDatePipe } from 'app/shared/date'; +import { FormsModule } from '@angular/forms'; +import { SORT, ITEM_DELETED_EVENT, DEFAULT_SORT_DATA } from 'app/config/navigation.constants'; import { IPreferences } from '../preferences.model'; import { EntityArrayResponseType, PreferencesService } from '../service/preferences.service'; import { PreferencesDeleteDialogComponent } from '../delete/preferences-delete-dialog.component'; @Component({ + standalone: true, selector: 'jhi-preferences', templateUrl: './preferences.component.html', + imports: [ + RouterModule, + FormsModule, + SharedModule, + SortDirective, + SortByDirective, + DurationPipe, + FormatMediumDatetimePipe, + FormatMediumDatePipe, + ], }) export class PreferencesComponent implements OnInit { private static readonly NOT_SORTABLE_FIELDS_AFTER_SEARCH = ['weightUnits']; + subscription: Subscription | null = null; preferences?: IPreferences[]; isLoading = false; - predicate = 'id'; - ascending = true; + sortState = sortStateSignal({}); currentSearch = ''; - constructor( - protected preferencesService: PreferencesService, - protected activatedRoute: ActivatedRoute, - public router: Router, - protected sortService: SortService, - protected modalService: NgbModal, - ) {} + public router = inject(Router); + protected preferencesService = inject(PreferencesService); + protected activatedRoute = inject(ActivatedRoute); + protected sortService = inject(SortService); + protected modalService = inject(NgbModal); + protected ngZone = inject(NgZone); trackId = (_index: number, item: IPreferences): number => this.preferencesService.getPreferencesIdentifier(item); + ngOnInit(): void { + this.subscription = combineLatest([this.activatedRoute.queryParamMap, this.activatedRoute.data]) + .pipe( + tap(([params, data]) => this.fillComponentAttributeFromRoute(params, data)), + tap(() => this.load()), + ) + .subscribe(); + } + search(query: string): void { - if (query && PreferencesComponent.NOT_SORTABLE_FIELDS_AFTER_SEARCH.includes(this.predicate)) { - this.predicate = 'id'; - this.ascending = true; + const { predicate } = this.sortState(); + if (query && predicate && PreferencesComponent.NOT_SORTABLE_FIELDS_AFTER_SEARCH.includes(predicate)) { + this.loadDefaultSortState(); } this.currentSearch = query; - this.navigateToWithComponentValues(); + this.navigateToWithComponentValues(this.sortState()); } - ngOnInit(): void { - this.load(); + loadDefaultSortState(): void { + this.sortState.set(this.sortService.parseSortParam(this.activatedRoute.snapshot.data[DEFAULT_SORT_DATA])); } delete(preferences: IPreferences): void { @@ -53,42 +75,30 @@ export class PreferencesComponent implements OnInit { modalRef.closed .pipe( filter(reason => reason === ITEM_DELETED_EVENT), - switchMap(() => this.loadFromBackendWithRouteInformations()), + tap(() => this.load()), ) - .subscribe({ - next: (res: EntityArrayResponseType) => { - this.onResponseSuccess(res); - }, - }); + .subscribe(); } load(): void { - this.loadFromBackendWithRouteInformations().subscribe({ + this.queryBackend().subscribe({ next: (res: EntityArrayResponseType) => { this.onResponseSuccess(res); }, }); } - navigateToWithComponentValues(): void { - this.handleNavigation(this.predicate, this.ascending, this.currentSearch); - } - - protected loadFromBackendWithRouteInformations(): Observable { - return combineLatest([this.activatedRoute.queryParamMap, this.activatedRoute.data]).pipe( - tap(([params, data]) => this.fillComponentAttributeFromRoute(params, data)), - switchMap(() => this.queryBackend(this.predicate, this.ascending, this.currentSearch)), - ); + navigateToWithComponentValues(event: SortState): void { + this.handleNavigation(event, this.currentSearch); } protected fillComponentAttributeFromRoute(params: ParamMap, data: Data): void { - const sort = (params.get(SORT) ?? data[DEFAULT_SORT_DATA]).split(','); - this.predicate = sort[0]; - this.ascending = sort[1] === ASC; + this.sortState.set(this.sortService.parseSortParam(params.get(SORT) ?? data[DEFAULT_SORT_DATA])); if (params.has('search') && params.get('search') !== '') { this.currentSearch = params.get('search') as string; - if (PreferencesComponent.NOT_SORTABLE_FIELDS_AFTER_SEARCH.includes(this.predicate)) { - this.predicate = ''; + const { predicate } = this.sortState(); + if (predicate && PreferencesComponent.NOT_SORTABLE_FIELDS_AFTER_SEARCH.includes(predicate)) { + this.sortState.set({}); } } } @@ -99,19 +109,22 @@ export class PreferencesComponent implements OnInit { } protected refineData(data: IPreferences[]): IPreferences[] { - return data.sort(this.sortService.startSort(this.predicate, this.ascending ? 1 : -1)); + const { predicate, order } = this.sortState(); + return predicate && order ? data.sort(this.sortService.startSort({ predicate, order })) : data; } protected fillComponentAttributesFromResponseBody(data: IPreferences[] | null): IPreferences[] { return data ?? []; } - protected queryBackend(predicate?: string, ascending?: boolean, currentSearch?: string): Observable { + protected queryBackend(): Observable { + const { currentSearch } = this; + this.isLoading = true; const queryObject: any = { eagerload: true, query: currentSearch, - sort: this.getSortQueryParam(predicate, ascending), + sort: this.sortService.buildSortParam(this.sortState()), }; if (this.currentSearch && this.currentSearch !== '') { return this.preferencesService.search(queryObject).pipe(tap(() => (this.isLoading = false))); @@ -120,24 +133,17 @@ export class PreferencesComponent implements OnInit { } } - protected handleNavigation(predicate?: string, ascending?: boolean, currentSearch?: string): void { + protected handleNavigation(sortState: SortState, currentSearch?: string): void { const queryParamsObj = { search: currentSearch, - sort: this.getSortQueryParam(predicate, ascending), + sort: this.sortService.buildSortParam(sortState), }; - this.router.navigate(['./'], { - relativeTo: this.activatedRoute, - queryParams: queryParamsObj, + this.ngZone.run(() => { + this.router.navigate(['./'], { + relativeTo: this.activatedRoute, + queryParams: queryParamsObj, + }); }); } - - protected getSortQueryParam(predicate = this.predicate, ascending = this.ascending): string[] { - const ascendingQueryParam = ascending ? ASC : DESC; - if (predicate === '') { - return []; - } else { - return [predicate + ',' + ascendingQueryParam]; - } - } } diff --git a/src/main/webapp/app/entities/preferences/preferences.model.ts b/src/main/webapp/app/entities/preferences/preferences.model.ts index 355f3dfb..711819cc 100644 --- a/src/main/webapp/app/entities/preferences/preferences.model.ts +++ b/src/main/webapp/app/entities/preferences/preferences.model.ts @@ -4,7 +4,7 @@ import { Units } from 'app/entities/enumerations/units.model'; export interface IPreferences { id: number; weeklyGoal?: number | null; - weightUnits?: Units | null; + weightUnits?: keyof typeof Units | null; user?: Pick | null; } diff --git a/src/main/webapp/app/entities/preferences/preferences.module.ts b/src/main/webapp/app/entities/preferences/preferences.module.ts deleted file mode 100644 index 1f5bd5ce..00000000 --- a/src/main/webapp/app/entities/preferences/preferences.module.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { NgModule } from '@angular/core'; -import { SharedModule } from 'app/shared/shared.module'; -import { PreferencesComponent } from './list/preferences.component'; -import { PreferencesDetailComponent } from './detail/preferences-detail.component'; -import { PreferencesUpdateComponent } from './update/preferences-update.component'; -import { PreferencesDeleteDialogComponent } from './delete/preferences-delete-dialog.component'; -import { PreferencesRoutingModule } from './route/preferences-routing.module'; - -@NgModule({ - imports: [SharedModule, PreferencesRoutingModule], - declarations: [PreferencesComponent, PreferencesDetailComponent, PreferencesUpdateComponent, PreferencesDeleteDialogComponent], -}) -export class PreferencesModule {} diff --git a/src/main/webapp/app/entities/preferences/route/preferences-routing.module.ts b/src/main/webapp/app/entities/preferences/preferences.routes.ts similarity index 50% rename from src/main/webapp/app/entities/preferences/route/preferences-routing.module.ts rename to src/main/webapp/app/entities/preferences/preferences.routes.ts index 6f4f4cae..ec931f3a 100644 --- a/src/main/webapp/app/entities/preferences/route/preferences-routing.module.ts +++ b/src/main/webapp/app/entities/preferences/preferences.routes.ts @@ -1,12 +1,11 @@ -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { Routes } from '@angular/router'; import { UserRouteAccessService } from 'app/core/auth/user-route-access.service'; import { ASC } from 'app/config/navigation.constants'; -import { PreferencesComponent } from '../list/preferences.component'; -import { PreferencesDetailComponent } from '../detail/preferences-detail.component'; -import { PreferencesUpdateComponent } from '../update/preferences-update.component'; -import { PreferencesRoutingResolveService } from './preferences-routing-resolve.service'; +import { PreferencesComponent } from './list/preferences.component'; +import { PreferencesDetailComponent } from './detail/preferences-detail.component'; +import { PreferencesUpdateComponent } from './update/preferences-update.component'; +import PreferencesResolve from './route/preferences-routing-resolve.service'; const preferencesRoute: Routes = [ { @@ -21,7 +20,7 @@ const preferencesRoute: Routes = [ path: ':id/view', component: PreferencesDetailComponent, resolve: { - preferences: PreferencesRoutingResolveService, + preferences: PreferencesResolve, }, canActivate: [UserRouteAccessService], }, @@ -29,7 +28,7 @@ const preferencesRoute: Routes = [ path: 'new', component: PreferencesUpdateComponent, resolve: { - preferences: PreferencesRoutingResolveService, + preferences: PreferencesResolve, }, canActivate: [UserRouteAccessService], }, @@ -37,14 +36,10 @@ const preferencesRoute: Routes = [ path: ':id/edit', component: PreferencesUpdateComponent, resolve: { - preferences: PreferencesRoutingResolveService, + preferences: PreferencesResolve, }, canActivate: [UserRouteAccessService], }, ]; -@NgModule({ - imports: [RouterModule.forChild(preferencesRoute)], - exports: [RouterModule], -}) -export class PreferencesRoutingModule {} +export default preferencesRoute; diff --git a/src/main/webapp/app/entities/preferences/preferences.test-samples.ts b/src/main/webapp/app/entities/preferences/preferences.test-samples.ts index 13e6d642..73a02e6a 100644 --- a/src/main/webapp/app/entities/preferences/preferences.test-samples.ts +++ b/src/main/webapp/app/entities/preferences/preferences.test-samples.ts @@ -1,28 +1,26 @@ -import { Units } from 'app/entities/enumerations/units.model'; - import { IPreferences, NewPreferences } from './preferences.model'; export const sampleWithRequiredData: IPreferences = { - id: 97604, - weeklyGoal: 13, - weightUnits: Units['LB'], + id: 11210, + weeklyGoal: 16, + weightUnits: 'LB', }; export const sampleWithPartialData: IPreferences = { - id: 73829, - weeklyGoal: 12, - weightUnits: Units['KG'], + id: 8696, + weeklyGoal: 20, + weightUnits: 'LB', }; export const sampleWithFullData: IPreferences = { - id: 80916, - weeklyGoal: 18, - weightUnits: Units['KG'], + id: 14979, + weeklyGoal: 15, + weightUnits: 'LB', }; export const sampleWithNewData: NewPreferences = { - weeklyGoal: 14, - weightUnits: Units['LB'], + weeklyGoal: 21, + weightUnits: 'KG', id: null, }; diff --git a/src/main/webapp/app/entities/preferences/route/preferences-routing-resolve.service.spec.ts b/src/main/webapp/app/entities/preferences/route/preferences-routing-resolve.service.spec.ts index fcb120a8..09f2a24a 100644 --- a/src/main/webapp/app/entities/preferences/route/preferences-routing-resolve.service.spec.ts +++ b/src/main/webapp/app/entities/preferences/route/preferences-routing-resolve.service.spec.ts @@ -2,24 +2,22 @@ import { TestBed } from '@angular/core/testing'; import { HttpResponse } from '@angular/common/http'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { ActivatedRouteSnapshot, ActivatedRoute, Router, convertToParamMap } from '@angular/router'; -import { RouterTestingModule } from '@angular/router/testing'; import { of } from 'rxjs'; import { IPreferences } from '../preferences.model'; import { PreferencesService } from '../service/preferences.service'; -import { PreferencesRoutingResolveService } from './preferences-routing-resolve.service'; +import preferencesResolve from './preferences-routing-resolve.service'; describe('Preferences routing resolve service', () => { let mockRouter: Router; let mockActivatedRouteSnapshot: ActivatedRouteSnapshot; - let routingResolveService: PreferencesRoutingResolveService; let service: PreferencesService; let resultPreferences: IPreferences | null | undefined; beforeEach(() => { TestBed.configureTestingModule({ - imports: [HttpClientTestingModule, RouterTestingModule.withRoutes([])], + imports: [HttpClientTestingModule], providers: [ { provide: ActivatedRoute, @@ -34,7 +32,6 @@ describe('Preferences routing resolve service', () => { mockRouter = TestBed.inject(Router); jest.spyOn(mockRouter, 'navigate').mockImplementation(() => Promise.resolve(true)); mockActivatedRouteSnapshot = TestBed.inject(ActivatedRoute).snapshot; - routingResolveService = TestBed.inject(PreferencesRoutingResolveService); service = TestBed.inject(PreferencesService); resultPreferences = undefined; }); @@ -46,8 +43,12 @@ describe('Preferences routing resolve service', () => { mockActivatedRouteSnapshot.params = { id: 123 }; // WHEN - routingResolveService.resolve(mockActivatedRouteSnapshot).subscribe(result => { - resultPreferences = result; + TestBed.runInInjectionContext(() => { + preferencesResolve(mockActivatedRouteSnapshot).subscribe({ + next(result) { + resultPreferences = result; + }, + }); }); // THEN @@ -61,8 +62,12 @@ describe('Preferences routing resolve service', () => { mockActivatedRouteSnapshot.params = {}; // WHEN - routingResolveService.resolve(mockActivatedRouteSnapshot).subscribe(result => { - resultPreferences = result; + TestBed.runInInjectionContext(() => { + preferencesResolve(mockActivatedRouteSnapshot).subscribe({ + next(result) { + resultPreferences = result; + }, + }); }); // THEN @@ -76,8 +81,12 @@ describe('Preferences routing resolve service', () => { mockActivatedRouteSnapshot.params = { id: 123 }; // WHEN - routingResolveService.resolve(mockActivatedRouteSnapshot).subscribe(result => { - resultPreferences = result; + TestBed.runInInjectionContext(() => { + preferencesResolve(mockActivatedRouteSnapshot).subscribe({ + next(result) { + resultPreferences = result; + }, + }); }); // THEN diff --git a/src/main/webapp/app/entities/preferences/route/preferences-routing-resolve.service.ts b/src/main/webapp/app/entities/preferences/route/preferences-routing-resolve.service.ts index 9fd3c14f..b33e94b9 100644 --- a/src/main/webapp/app/entities/preferences/route/preferences-routing-resolve.service.ts +++ b/src/main/webapp/app/entities/preferences/route/preferences-routing-resolve.service.ts @@ -1,33 +1,29 @@ -import { Injectable } from '@angular/core'; +import { inject } from '@angular/core'; import { HttpResponse } from '@angular/common/http'; -import { Resolve, ActivatedRouteSnapshot, Router } from '@angular/router'; -import { Observable, of, EMPTY } from 'rxjs'; +import { ActivatedRouteSnapshot, Router } from '@angular/router'; +import { of, EMPTY, Observable } from 'rxjs'; import { mergeMap } from 'rxjs/operators'; import { IPreferences } from '../preferences.model'; import { PreferencesService } from '../service/preferences.service'; -@Injectable({ providedIn: 'root' }) -export class PreferencesRoutingResolveService implements Resolve { - constructor( - protected service: PreferencesService, - protected router: Router, - ) {} - - resolve(route: ActivatedRouteSnapshot): Observable { - const id = route.params['id']; - if (id) { - return this.service.find(id).pipe( +const preferencesResolve = (route: ActivatedRouteSnapshot): Observable => { + const id = route.params['id']; + if (id) { + return inject(PreferencesService) + .find(id) + .pipe( mergeMap((preferences: HttpResponse) => { if (preferences.body) { return of(preferences.body); } else { - this.router.navigate(['404']); + inject(Router).navigate(['404']); return EMPTY; } }), ); - } - return of(null); } -} + return of(null); +}; + +export default preferencesResolve; diff --git a/src/main/webapp/app/entities/preferences/service/preferences.service.spec.ts b/src/main/webapp/app/entities/preferences/service/preferences.service.spec.ts index 9e821f30..1404a0c9 100644 --- a/src/main/webapp/app/entities/preferences/service/preferences.service.spec.ts +++ b/src/main/webapp/app/entities/preferences/service/preferences.service.spec.ts @@ -37,7 +37,6 @@ describe('Preferences Service', () => { }); it('should create a Preferences', () => { - // eslint-disable-next-line @typescript-eslint/no-unused-vars const preferences = { ...sampleWithNewData }; const returnedFromService = { ...requireRestSample }; const expected = { ...sampleWithRequiredData }; @@ -96,6 +95,20 @@ describe('Preferences Service', () => { expect(expectedResult).toBe(expected); }); + it('should handle exceptions for searching a Preferences', () => { + const queryObject: any = { + page: 0, + size: 20, + query: '', + sort: [], + }; + service.search(queryObject).subscribe(() => expectedResult); + + const req = httpMock.expectOne({ method: 'GET' }); + req.flush(null, { status: 500, statusText: 'Internal Server Error' }); + expect(expectedResult).toBe(null); + }); + describe('addPreferencesToCollectionIfMissing', () => { it('should add a Preferences to an empty array', () => { const preferences: IPreferences = sampleWithRequiredData; diff --git a/src/main/webapp/app/entities/preferences/service/preferences.service.ts b/src/main/webapp/app/entities/preferences/service/preferences.service.ts index d5d57d07..e4d15d96 100644 --- a/src/main/webapp/app/entities/preferences/service/preferences.service.ts +++ b/src/main/webapp/app/entities/preferences/service/preferences.service.ts @@ -1,6 +1,8 @@ -import { Injectable } from '@angular/core'; +import { inject, Injectable } from '@angular/core'; import { HttpClient, HttpResponse } from '@angular/common/http'; -import { Observable } from 'rxjs'; +import { Observable, asapScheduler, scheduled } from 'rxjs'; + +import { catchError } from 'rxjs/operators'; import { isPresent } from 'app/core/util/operators'; import { ApplicationConfigService } from 'app/core/config/application-config.service'; @@ -15,13 +17,11 @@ export type EntityArrayResponseType = HttpResponse; @Injectable({ providedIn: 'root' }) export class PreferencesService { - protected resourceUrl = this.applicationConfigService.getEndpointFor('api/preferences'); - protected resourceSearchUrl = this.applicationConfigService.getEndpointFor('api/_search/preferences'); + protected http = inject(HttpClient); + protected applicationConfigService = inject(ApplicationConfigService); - constructor( - protected http: HttpClient, - protected applicationConfigService: ApplicationConfigService, - ) {} + protected resourceUrl = this.applicationConfigService.getEndpointFor('api/preferences'); + protected resourceSearchUrl = this.applicationConfigService.getEndpointFor('api/preferences/_search'); create(preferences: NewPreferences): Observable { return this.http.post(this.resourceUrl, preferences, { observe: 'response' }); @@ -54,7 +54,9 @@ export class PreferencesService { search(req: Search): Observable { const options = createRequestOption(req); - return this.http.get(this.resourceSearchUrl, { params: options, observe: 'response' }); + return this.http + .get(this.resourceSearchUrl, { params: options, observe: 'response' }) + .pipe(catchError(() => scheduled([new HttpResponse()], asapScheduler))); } getPreferencesIdentifier(preferences: Pick): number { @@ -71,9 +73,7 @@ export class PreferencesService { ): Type[] { const preferences: Type[] = preferencesToCheck.filter(isPresent); if (preferences.length > 0) { - const preferencesCollectionIdentifiers = preferencesCollection.map( - preferencesItem => this.getPreferencesIdentifier(preferencesItem)!, - ); + const preferencesCollectionIdentifiers = preferencesCollection.map(preferencesItem => this.getPreferencesIdentifier(preferencesItem)); const preferencesToAdd = preferences.filter(preferencesItem => { const preferencesIdentifier = this.getPreferencesIdentifier(preferencesItem); if (preferencesCollectionIdentifiers.includes(preferencesIdentifier)) { diff --git a/src/main/webapp/app/entities/preferences/update/preferences-form.service.spec.ts b/src/main/webapp/app/entities/preferences/update/preferences-form.service.spec.ts index 0c373146..1a04f252 100644 --- a/src/main/webapp/app/entities/preferences/update/preferences-form.service.spec.ts +++ b/src/main/webapp/app/entities/preferences/update/preferences-form.service.spec.ts @@ -43,7 +43,6 @@ describe('Preferences Form Service', () => { describe('getPreferences', () => { it('should return NewPreferences for default Preferences initial value', () => { - // eslint-disable-next-line @typescript-eslint/no-unused-vars const formGroup = service.createPreferencesFormGroup(sampleWithNewData); const preferences = service.getPreferences(formGroup) as any; diff --git a/src/main/webapp/app/entities/preferences/update/preferences-update.component.html b/src/main/webapp/app/entities/preferences/update/preferences-update.component.html index e44e160e..82473f62 100644 --- a/src/main/webapp/app/entities/preferences/update/preferences-update.component.html +++ b/src/main/webapp/app/entities/preferences/update/preferences-update.component.html @@ -1,6 +1,6 @@
-
+

-
- - -
+ @if (editForm.controls.id.value !== null) { +
+ + +
+ } -
- +
+ -
- - This field is required. - - - This field should be at least 10. - - - This field cannot be more than 21. - - - This field should be a number. - -
+ @if (editForm.get('weeklyGoal')!.invalid && (editForm.get('weeklyGoal')!.dirty || editForm.get('weeklyGoal')!.touched)) { +
+ @if (editForm.get('weeklyGoal')?.errors?.required) { + This field is required. + } + @if (editForm.get('weeklyGoal')?.errors?.min) { + This field should be at least 10. + } + @if (editForm.get('weeklyGoal')?.errors?.max) { + This field cannot be more than 21. + } + This field should be a number. +
+ }
-
- +
+ -
- - This field is required. - -
+ @if (editForm.get('weightUnits')!.invalid && (editForm.get('weightUnits')!.dirty || editForm.get('weightUnits')!.touched)) { +
+ @if (editForm.get('weightUnits')?.errors?.required) { + This field is required. + } +
+ }
-
- +
+
diff --git a/src/main/webapp/app/entities/preferences/update/preferences-update.component.spec.ts b/src/main/webapp/app/entities/preferences/update/preferences-update.component.spec.ts index b11297d0..2e5aea9e 100644 --- a/src/main/webapp/app/entities/preferences/update/preferences-update.component.spec.ts +++ b/src/main/webapp/app/entities/preferences/update/preferences-update.component.spec.ts @@ -3,14 +3,12 @@ import { HttpResponse } from '@angular/common/http'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { FormBuilder } from '@angular/forms'; import { ActivatedRoute } from '@angular/router'; -import { RouterTestingModule } from '@angular/router/testing'; import { of, Subject, from } from 'rxjs'; import { IUser } from 'app/entities/user/user.model'; -import { UserService } from 'app/entities/user/user.service'; +import { UserService } from 'app/entities/user/service/user.service'; import { PreferencesService } from '../service/preferences.service'; import { IPreferences } from '../preferences.model'; - import { PreferencesFormService } from './preferences-form.service'; import { PreferencesUpdateComponent } from './preferences-update.component'; @@ -25,8 +23,7 @@ describe('Preferences Management Update Component', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [HttpClientTestingModule, RouterTestingModule.withRoutes([])], - declarations: [PreferencesUpdateComponent], + imports: [HttpClientTestingModule, PreferencesUpdateComponent], providers: [ FormBuilder, { @@ -52,10 +49,10 @@ describe('Preferences Management Update Component', () => { describe('ngOnInit', () => { it('Should call User query and add missing value', () => { const preferences: IPreferences = { id: 456 }; - const user: IUser = { id: 76797 }; + const user: IUser = { id: 15163 }; preferences.user = user; - const userCollection: IUser[] = [{ id: 99188 }]; + const userCollection: IUser[] = [{ id: 24024 }]; jest.spyOn(userService, 'query').mockReturnValue(of(new HttpResponse({ body: userCollection }))); const additionalUsers = [user]; const expectedCollection: IUser[] = [...additionalUsers, ...userCollection]; @@ -74,7 +71,7 @@ describe('Preferences Management Update Component', () => { it('Should update editForm', () => { const preferences: IPreferences = { id: 456 }; - const user: IUser = { id: 79175 }; + const user: IUser = { id: 3932 }; preferences.user = user; activatedRoute.data = of({ preferences }); diff --git a/src/main/webapp/app/entities/preferences/update/preferences-update.component.ts b/src/main/webapp/app/entities/preferences/update/preferences-update.component.ts index 1eec2162..3ca54395 100644 --- a/src/main/webapp/app/entities/preferences/update/preferences-update.component.ts +++ b/src/main/webapp/app/entities/preferences/update/preferences-update.component.ts @@ -1,19 +1,24 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, inject, OnInit } from '@angular/core'; import { HttpResponse } from '@angular/common/http'; import { ActivatedRoute } from '@angular/router'; import { Observable } from 'rxjs'; import { finalize, map } from 'rxjs/operators'; +import SharedModule from 'app/shared/shared.module'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; + import { IUser } from 'app/entities/user/user.model'; -import { UserService } from 'app/entities/user/user.service'; +import { UserService } from 'app/entities/user/service/user.service'; import { Units } from 'app/entities/enumerations/units.model'; import { PreferencesService } from '../service/preferences.service'; import { IPreferences } from '../preferences.model'; import { PreferencesFormService, PreferencesFormGroup } from './preferences-form.service'; @Component({ + standalone: true, selector: 'jhi-preferences-update', templateUrl: './preferences-update.component.html', + imports: [SharedModule, FormsModule, ReactiveFormsModule], }) export class PreferencesUpdateComponent implements OnInit { isSaving = false; @@ -22,14 +27,13 @@ export class PreferencesUpdateComponent implements OnInit { usersSharedCollection: IUser[] = []; - editForm: PreferencesFormGroup = this.preferencesFormService.createPreferencesFormGroup(); + protected preferencesService = inject(PreferencesService); + protected preferencesFormService = inject(PreferencesFormService); + protected userService = inject(UserService); + protected activatedRoute = inject(ActivatedRoute); - constructor( - protected preferencesService: PreferencesService, - protected preferencesFormService: PreferencesFormService, - protected userService: UserService, - protected activatedRoute: ActivatedRoute, - ) {} + // eslint-disable-next-line @typescript-eslint/member-ordering + editForm: PreferencesFormGroup = this.preferencesFormService.createPreferencesFormGroup(); compareUser = (o1: IUser | null, o2: IUser | null): boolean => this.userService.compareUser(o1, o2); diff --git a/src/main/webapp/app/entities/user/service/user.service.spec.ts b/src/main/webapp/app/entities/user/service/user.service.spec.ts new file mode 100644 index 00000000..53c8a05e --- /dev/null +++ b/src/main/webapp/app/entities/user/service/user.service.spec.ts @@ -0,0 +1,172 @@ +import { TestBed } from '@angular/core/testing'; +import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; + +import { IUser } from '../user.model'; +import { sampleWithRequiredData, sampleWithPartialData, sampleWithFullData } from '../user.test-samples'; + +import { UserService } from './user.service'; + +const requireRestSample: IUser = { + ...sampleWithRequiredData, +}; + +describe('User Service', () => { + let service: UserService; + let httpMock: HttpTestingController; + let expectedResult: IUser | IUser[] | boolean | null; + + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [HttpClientTestingModule], + }); + expectedResult = null; + service = TestBed.inject(UserService); + httpMock = TestBed.inject(HttpTestingController); + }); + + describe('Service methods', () => { + it('should find an element', () => { + const returnedFromService = { ...requireRestSample }; + const expected = { ...sampleWithRequiredData }; + + service.find(123).subscribe(resp => (expectedResult = resp.body)); + + const req = httpMock.expectOne({ method: 'GET' }); + req.flush(returnedFromService); + expect(expectedResult).toMatchObject(expected); + }); + + it('should return a list of User', () => { + const returnedFromService = { ...requireRestSample }; + + const expected = { ...sampleWithRequiredData }; + + service.query().subscribe(resp => (expectedResult = resp.body)); + + const req = httpMock.expectOne({ method: 'GET' }); + req.flush([returnedFromService]); + httpMock.verify(); + expect(expectedResult).toMatchObject([expected]); + }); + + it('should handle exceptions for searching a User', () => { + const queryObject: any = { + page: 0, + size: 20, + query: '', + sort: [], + }; + service.search(queryObject).subscribe(() => expectedResult); + + const req = httpMock.expectOne({ method: 'GET' }); + req.flush(null, { status: 500, statusText: 'Internal Server Error' }); + expect(expectedResult).toBe(null); + }); + + describe('addUserToCollectionIfMissing', () => { + it('should add a User to an empty array', () => { + const user: IUser = sampleWithRequiredData; + expectedResult = service.addUserToCollectionIfMissing([], user); + expect(expectedResult).toHaveLength(1); + expect(expectedResult).toContain(user); + }); + + it('should not add a User to an array that contains it', () => { + const user: IUser = sampleWithRequiredData; + const userCollection: IUser[] = [ + { + ...user, + }, + sampleWithPartialData, + ]; + expectedResult = service.addUserToCollectionIfMissing(userCollection, user); + expect(expectedResult).toHaveLength(2); + }); + + it("should add a User to an array that doesn't contain it", () => { + const user: IUser = sampleWithRequiredData; + const userCollection: IUser[] = [sampleWithPartialData]; + expectedResult = service.addUserToCollectionIfMissing(userCollection, user); + expect(expectedResult).toHaveLength(2); + expect(expectedResult).toContain(user); + }); + + it('should add only unique User to an array', () => { + const userArray: IUser[] = [sampleWithRequiredData, sampleWithPartialData, sampleWithFullData]; + const userCollection: IUser[] = [sampleWithRequiredData]; + expectedResult = service.addUserToCollectionIfMissing(userCollection, ...userArray); + expect(expectedResult).toHaveLength(3); + }); + + it('should accept varargs', () => { + const user: IUser = sampleWithRequiredData; + const user2: IUser = sampleWithPartialData; + expectedResult = service.addUserToCollectionIfMissing([], user, user2); + expect(expectedResult).toHaveLength(2); + expect(expectedResult).toContain(user); + expect(expectedResult).toContain(user2); + }); + + it('should accept null and undefined values', () => { + const user: IUser = sampleWithRequiredData; + expectedResult = service.addUserToCollectionIfMissing([], null, user, undefined); + expect(expectedResult).toHaveLength(1); + expect(expectedResult).toContain(user); + }); + + it('should return initial array if no User is added', () => { + const userCollection: IUser[] = [sampleWithRequiredData]; + expectedResult = service.addUserToCollectionIfMissing(userCollection, undefined, null); + expect(expectedResult).toEqual(userCollection); + }); + }); + + describe('compareUser', () => { + it('Should return true if both entities are null', () => { + const entity1 = null; + const entity2 = null; + + const compareResult = service.compareUser(entity1, entity2); + + expect(compareResult).toEqual(true); + }); + + it('Should return false if one entity is null', () => { + const entity1 = { id: 123 }; + const entity2 = null; + + const compareResult1 = service.compareUser(entity1, entity2); + const compareResult2 = service.compareUser(entity2, entity1); + + expect(compareResult1).toEqual(false); + expect(compareResult2).toEqual(false); + }); + + it('Should return false if primaryKey differs', () => { + const entity1 = { id: 123 }; + const entity2 = { id: 456 }; + + const compareResult1 = service.compareUser(entity1, entity2); + const compareResult2 = service.compareUser(entity2, entity1); + + expect(compareResult1).toEqual(false); + expect(compareResult2).toEqual(false); + }); + + it('Should return false if primaryKey matches', () => { + const entity1 = { id: 123 }; + const entity2 = { id: 123 }; + + const compareResult1 = service.compareUser(entity1, entity2); + const compareResult2 = service.compareUser(entity2, entity1); + + expect(compareResult1).toEqual(true); + expect(compareResult2).toEqual(true); + }); + }); + }); + + afterEach(() => { + httpMock.verify(); + }); +}); diff --git a/src/main/webapp/app/entities/user/service/user.service.ts b/src/main/webapp/app/entities/user/service/user.service.ts new file mode 100644 index 00000000..b809527e --- /dev/null +++ b/src/main/webapp/app/entities/user/service/user.service.ts @@ -0,0 +1,67 @@ +import { inject, Injectable } from '@angular/core'; +import { HttpClient, HttpResponse } from '@angular/common/http'; +import { Observable, asapScheduler, scheduled } from 'rxjs'; + +import { catchError } from 'rxjs/operators'; + +import { isPresent } from 'app/core/util/operators'; +import { ApplicationConfigService } from 'app/core/config/application-config.service'; +import { createRequestOption } from 'app/core/request/request-util'; +import { SearchWithPagination } from 'app/core/request/request.model'; +import { IUser } from '../user.model'; + +export type EntityResponseType = HttpResponse; +export type EntityArrayResponseType = HttpResponse; + +@Injectable({ providedIn: 'root' }) +export class UserService { + protected http = inject(HttpClient); + protected applicationConfigService = inject(ApplicationConfigService); + + protected resourceUrl = this.applicationConfigService.getEndpointFor('api/users'); + protected resourceSearchUrl = this.applicationConfigService.getEndpointFor('api/users/_search'); + + find(id: number): Observable { + return this.http.get(`${this.resourceUrl}/${id}`, { observe: 'response' }); + } + + query(req?: any): Observable { + const options = createRequestOption(req); + return this.http.get(this.resourceUrl, { params: options, observe: 'response' }); + } + + search(req: SearchWithPagination): Observable { + const options = createRequestOption(req); + return this.http + .get(this.resourceSearchUrl, { params: options, observe: 'response' }) + .pipe(catchError(() => scheduled([new HttpResponse()], asapScheduler))); + } + + getUserIdentifier(user: Pick): number { + return user.id; + } + + compareUser(o1: Pick | null, o2: Pick | null): boolean { + return o1 && o2 ? this.getUserIdentifier(o1) === this.getUserIdentifier(o2) : o1 === o2; + } + + addUserToCollectionIfMissing>( + userCollection: Type[], + ...usersToCheck: (Type | null | undefined)[] + ): Type[] { + const users: Type[] = usersToCheck.filter(isPresent); + if (users.length > 0) { + const userCollectionIdentifiers = userCollection.map(userItem => this.getUserIdentifier(userItem)); + const usersToAdd = users.filter(userItem => { + const userIdentifier = this.getUserIdentifier(userItem); + if (userCollectionIdentifiers.includes(userIdentifier)) { + return false; + } + userCollectionIdentifiers.push(userIdentifier); + return true; + }); + return [...usersToAdd, ...userCollection]; + } + return userCollection; + } +} diff --git a/src/main/webapp/app/entities/user/user.model.ts b/src/main/webapp/app/entities/user/user.model.ts index 74137a9b..fc704a49 100644 --- a/src/main/webapp/app/entities/user/user.model.ts +++ b/src/main/webapp/app/entities/user/user.model.ts @@ -1,15 +1,4 @@ export interface IUser { id: number; - login?: string; -} - -export class User implements IUser { - constructor( - public id: number, - public login: string, - ) {} -} - -export function getUserIdentifier(user: IUser): number { - return user.id; + login?: string | null; } diff --git a/src/main/webapp/app/entities/user/user.service.spec.ts b/src/main/webapp/app/entities/user/user.service.spec.ts deleted file mode 100644 index 7e7d9849..00000000 --- a/src/main/webapp/app/entities/user/user.service.spec.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { TestBed } from '@angular/core/testing'; -import { HttpErrorResponse } from '@angular/common/http'; -import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; - -import { User, IUser } from './user.model'; - -import { UserService } from './user.service'; - -describe('User Service', () => { - let service: UserService; - let httpMock: HttpTestingController; - let expectedResult: IUser | IUser[] | boolean | number | null; - - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [HttpClientTestingModule], - }); - expectedResult = null; - service = TestBed.inject(UserService); - httpMock = TestBed.inject(HttpTestingController); - }); - - afterEach(() => { - httpMock.verify(); - }); - - describe('Service methods', () => { - it('should return Users', () => { - service.query().subscribe(received => { - expectedResult = received.body; - }); - - const req = httpMock.expectOne({ method: 'GET' }); - req.flush([new User(123, 'user')]); - expect(expectedResult).toEqual([{ id: 123, login: 'user' }]); - }); - - it('should propagate not found response', () => { - service.query().subscribe({ - error: (error: HttpErrorResponse) => (expectedResult = error.status), - }); - - const req = httpMock.expectOne({ method: 'GET' }); - req.flush('Internal Server Error', { - status: 500, - statusText: 'Inernal Server Error', - }); - expect(expectedResult).toEqual(500); - }); - - describe('addUserToCollectionIfMissing', () => { - it('should add a User to an empty array', () => { - const user: IUser = { id: 123 }; - expectedResult = service.addUserToCollectionIfMissing([], user); - expect(expectedResult).toHaveLength(1); - expect(expectedResult).toContain(user); - }); - - it('should not add a User to an array that contains it', () => { - const user: IUser = { id: 123 }; - const userCollection: IUser[] = [ - { - ...user, - }, - { id: 456 }, - ]; - expectedResult = service.addUserToCollectionIfMissing(userCollection, user); - expect(expectedResult).toHaveLength(2); - }); - - it("should add a User to an array that doesn't contain it", () => { - const user: IUser = { id: 123 }; - const userCollection: IUser[] = [{ id: 456 }]; - expectedResult = service.addUserToCollectionIfMissing(userCollection, user); - expect(expectedResult).toHaveLength(2); - expect(expectedResult).toContain(user); - }); - - it('should add only unique User to an array', () => { - const userArray: IUser[] = [{ id: 123 }, { id: 456 }, { id: 27699 }]; - const userCollection: IUser[] = [{ id: 456 }]; - expectedResult = service.addUserToCollectionIfMissing(userCollection, ...userArray); - expect(expectedResult).toHaveLength(3); - }); - - it('should accept varargs', () => { - const user: IUser = { id: 123 }; - const user2: IUser = { id: 456 }; - expectedResult = service.addUserToCollectionIfMissing([], user, user2); - expect(expectedResult).toHaveLength(2); - expect(expectedResult).toContain(user); - expect(expectedResult).toContain(user2); - }); - - it('should accept null and undefined values', () => { - const user: IUser = { id: 123 }; - expectedResult = service.addUserToCollectionIfMissing([], null, user, undefined); - expect(expectedResult).toHaveLength(1); - expect(expectedResult).toContain(user); - }); - - it('should return initial array if no users is added', () => { - const userCollection: IUser[] = [{ id: 456 }]; - expectedResult = service.addUserToCollectionIfMissing(userCollection, null, undefined); - expect(expectedResult).toEqual(userCollection); - }); - }); - }); -}); diff --git a/src/main/webapp/app/entities/user/user.service.ts b/src/main/webapp/app/entities/user/user.service.ts deleted file mode 100644 index 02f2c659..00000000 --- a/src/main/webapp/app/entities/user/user.service.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { Injectable } from '@angular/core'; -import { HttpClient, HttpResponse } from '@angular/common/http'; -import { Observable } from 'rxjs'; - -import { ApplicationConfigService } from 'app/core/config/application-config.service'; -import { createRequestOption } from 'app/core/request/request-util'; -import { isPresent } from 'app/core/util/operators'; -import { Pagination } from 'app/core/request/request.model'; -import { IUser, getUserIdentifier } from './user.model'; - -@Injectable({ providedIn: 'root' }) -export class UserService { - private resourceUrl = this.applicationConfigService.getEndpointFor('api/users'); - - constructor( - private http: HttpClient, - private applicationConfigService: ApplicationConfigService, - ) {} - - query(req?: Pagination): Observable> { - const options = createRequestOption(req); - return this.http.get(this.resourceUrl, { params: options, observe: 'response' }); - } - - compareUser(o1: Pick | null, o2: Pick | null): boolean { - return o1 && o2 ? o1.id === o2.id : o1 === o2; - } - - addUserToCollectionIfMissing & Pick>( - userCollection: Type[], - ...usersToCheck: (Type | null | undefined)[] - ): IUser[] { - const users: Type[] = usersToCheck.filter(isPresent); - if (users.length > 0) { - const userCollectionIdentifiers = userCollection.map(userItem => getUserIdentifier(userItem)!); - const usersToAdd = users.filter(userItem => { - const userIdentifier = getUserIdentifier(userItem); - if (userCollectionIdentifiers.includes(userIdentifier)) { - return false; - } - userCollectionIdentifiers.push(userIdentifier); - return true; - }); - return [...usersToAdd, ...userCollection]; - } - return userCollection; - } -} diff --git a/src/main/webapp/app/entities/user/user.test-samples.ts b/src/main/webapp/app/entities/user/user.test-samples.ts new file mode 100644 index 00000000..259a8454 --- /dev/null +++ b/src/main/webapp/app/entities/user/user.test-samples.ts @@ -0,0 +1,19 @@ +import { IUser } from './user.model'; + +export const sampleWithRequiredData: IUser = { + id: 336, + login: 'dMUa~@bSCRjq\\#ND-K3-', +}; + +export const sampleWithPartialData: IUser = { + id: 13843, + login: 'by', +}; + +export const sampleWithFullData: IUser = { + id: 6007, + login: '9iF=@l\\_R411', +}; +Object.freeze(sampleWithRequiredData); +Object.freeze(sampleWithPartialData); +Object.freeze(sampleWithFullData); diff --git a/src/main/webapp/app/entities/weight/delete/weight-delete-dialog.component.html b/src/main/webapp/app/entities/weight/delete/weight-delete-dialog.component.html index 5a2b3a6e..ccdabc5e 100644 --- a/src/main/webapp/app/entities/weight/delete/weight-delete-dialog.component.html +++ b/src/main/webapp/app/entities/weight/delete/weight-delete-dialog.component.html @@ -1,24 +1,26 @@ - - - + - + +} diff --git a/src/main/webapp/app/entities/weight/delete/weight-delete-dialog.component.spec.ts b/src/main/webapp/app/entities/weight/delete/weight-delete-dialog.component.spec.ts index 7a2e2e60..70241a8d 100644 --- a/src/main/webapp/app/entities/weight/delete/weight-delete-dialog.component.spec.ts +++ b/src/main/webapp/app/entities/weight/delete/weight-delete-dialog.component.spec.ts @@ -18,8 +18,7 @@ describe('Weight Management Delete Component', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [HttpClientTestingModule], - declarations: [WeightDeleteDialogComponent], + imports: [HttpClientTestingModule, WeightDeleteDialogComponent], providers: [NgbActiveModal], }) .overrideTemplate(WeightDeleteDialogComponent, '') diff --git a/src/main/webapp/app/entities/weight/delete/weight-delete-dialog.component.ts b/src/main/webapp/app/entities/weight/delete/weight-delete-dialog.component.ts index 25104118..1054068c 100644 --- a/src/main/webapp/app/entities/weight/delete/weight-delete-dialog.component.ts +++ b/src/main/webapp/app/entities/weight/delete/weight-delete-dialog.component.ts @@ -1,20 +1,22 @@ -import { Component } from '@angular/core'; +import { Component, inject } from '@angular/core'; +import { FormsModule } from '@angular/forms'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; +import SharedModule from 'app/shared/shared.module'; import { ITEM_DELETED_EVENT } from 'app/config/navigation.constants'; import { IWeight } from '../weight.model'; import { WeightService } from '../service/weight.service'; @Component({ + standalone: true, templateUrl: './weight-delete-dialog.component.html', + imports: [SharedModule, FormsModule], }) export class WeightDeleteDialogComponent { weight?: IWeight; - constructor( - protected weightService: WeightService, - protected activeModal: NgbActiveModal, - ) {} + protected weightService = inject(WeightService); + protected activeModal = inject(NgbActiveModal); cancel(): void { this.activeModal.dismiss(); diff --git a/src/main/webapp/app/entities/weight/detail/weight-detail.component.html b/src/main/webapp/app/entities/weight/detail/weight-detail.component.html index 630b0d33..d484665c 100644 --- a/src/main/webapp/app/entities/weight/detail/weight-detail.component.html +++ b/src/main/webapp/app/entities/weight/detail/weight-detail.component.html @@ -1,40 +1,42 @@
-
-

Weight

+ @if (weight()) { +
+

Weight

-
+
- + - + -
-
ID
-
- {{ weight.id }} -
-
Timestamp
-
- {{ weight.timestamp | formatMediumDatetime }} -
-
Weight
-
- {{ weight.weight }} -
-
User
-
- {{ weight.user?.login }} -
-
+
+
ID
+
+ {{ weight()!.id }} +
+
Timestamp
+
+ {{ weight()!.timestamp | formatMediumDatetime }} +
+
Weight
+
+ {{ weight()!.weight }} +
+
User
+
+ {{ weight()!.user?.login }} +
+
- + - -
+ +
+ }
diff --git a/src/main/webapp/app/entities/weight/detail/weight-detail.component.spec.ts b/src/main/webapp/app/entities/weight/detail/weight-detail.component.spec.ts index a041dfc9..4721d09b 100644 --- a/src/main/webapp/app/entities/weight/detail/weight-detail.component.spec.ts +++ b/src/main/webapp/app/entities/weight/detail/weight-detail.component.spec.ts @@ -1,5 +1,6 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; -import { ActivatedRoute } from '@angular/router'; +import { provideRouter, withComponentInputBinding } from '@angular/router'; +import { RouterTestingHarness } from '@angular/router/testing'; import { of } from 'rxjs'; import { WeightDetailComponent } from './weight-detail.component'; @@ -8,29 +9,46 @@ describe('Weight Management Detail Component', () => { let comp: WeightDetailComponent; let fixture: ComponentFixture; - beforeEach(() => { - TestBed.configureTestingModule({ - declarations: [WeightDetailComponent], + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [WeightDetailComponent], providers: [ - { - provide: ActivatedRoute, - useValue: { data: of({ weight: { id: 123 } }) }, - }, + provideRouter( + [ + { + path: '**', + component: WeightDetailComponent, + resolve: { weight: () => of({ id: 123 }) }, + }, + ], + withComponentInputBinding(), + ), ], }) .overrideTemplate(WeightDetailComponent, '') .compileComponents(); + }); + + beforeEach(() => { fixture = TestBed.createComponent(WeightDetailComponent); comp = fixture.componentInstance; }); describe('OnInit', () => { - it('Should load weight on init', () => { - // WHEN - comp.ngOnInit(); + it('Should load weight on init', async () => { + const harness = await RouterTestingHarness.create(); + const instance = await harness.navigateByUrl('/', WeightDetailComponent); // THEN - expect(comp.weight).toEqual(expect.objectContaining({ id: 123 })); + expect(instance.weight()).toEqual(expect.objectContaining({ id: 123 })); + }); + }); + + describe('PreviousState', () => { + it('Should navigate to previous state', () => { + jest.spyOn(window.history, 'back'); + comp.previousState(); + expect(window.history.back).toHaveBeenCalled(); }); }); }); diff --git a/src/main/webapp/app/entities/weight/detail/weight-detail.component.ts b/src/main/webapp/app/entities/weight/detail/weight-detail.component.ts index 7865c77f..3a78208d 100644 --- a/src/main/webapp/app/entities/weight/detail/weight-detail.component.ts +++ b/src/main/webapp/app/entities/weight/detail/weight-detail.component.ts @@ -1,22 +1,18 @@ -import { Component, OnInit } from '@angular/core'; -import { ActivatedRoute } from '@angular/router'; +import { Component, input } from '@angular/core'; +import { RouterModule } from '@angular/router'; +import SharedModule from 'app/shared/shared.module'; +import { DurationPipe, FormatMediumDatetimePipe, FormatMediumDatePipe } from 'app/shared/date'; import { IWeight } from '../weight.model'; @Component({ + standalone: true, selector: 'jhi-weight-detail', templateUrl: './weight-detail.component.html', + imports: [SharedModule, RouterModule, DurationPipe, FormatMediumDatetimePipe, FormatMediumDatePipe], }) -export class WeightDetailComponent implements OnInit { - weight: IWeight | null = null; - - constructor(protected activatedRoute: ActivatedRoute) {} - - ngOnInit(): void { - this.activatedRoute.data.subscribe(({ weight }) => { - this.weight = weight; - }); - } +export class WeightDetailComponent { + weight = input(null); previousState(): void { window.history.back(); diff --git a/src/main/webapp/app/entities/weight/list/weight.component.html b/src/main/webapp/app/entities/weight/list/weight.component.html index a8a7f686..ae21ff20 100644 --- a/src/main/webapp/app/entities/weight/list/weight.component.html +++ b/src/main/webapp/app/entities/weight/list/weight.component.html @@ -15,7 +15,7 @@

[routerLink]="['/weight/new']" > - Create a new Weight + Create a new Weight

@@ -41,83 +41,89 @@

- + @if (currentSearch) { + + }

-
- No Weights found -
+ @if (weights?.length === 0) { +
+ No Weights found +
+ } + + @if (weights && weights.length > 0) { +
+ + + + + + + + + + @for (weight of weights; track trackId) { + + + + + + + + } + +
+
+ ID -
- - - - - - - - - - - - - - - - - + - -
-
- ID - -
-
-
- Timestamp - -
-
-
- Weight - -
-
-
- User - -
-
- {{ weight.id }} - {{ weight.timestamp | formatMediumDatetime }}{{ weight.weight }} - {{ weight.user?.login }} - -
- + +
+ +
+
+ Timestamp - + +
+
+
+ Weight - -
- -
-
+ +
+
+
+ User + +
+
+ {{ weight.id }} + {{ weight.timestamp | formatMediumDatetime }}{{ weight.weight }} + {{ weight.user?.login }} + +
+ + + View + + + + + Edit + + + +
+
+
+ }
diff --git a/src/main/webapp/app/entities/weight/list/weight.component.spec.ts b/src/main/webapp/app/entities/weight/list/weight.component.spec.ts index f81c863c..1af6ddba 100644 --- a/src/main/webapp/app/entities/weight/list/weight.component.spec.ts +++ b/src/main/webapp/app/entities/weight/list/weight.component.spec.ts @@ -1,10 +1,11 @@ -import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { ComponentFixture, TestBed, fakeAsync, inject, tick } from '@angular/core/testing'; import { HttpHeaders, HttpResponse } from '@angular/common/http'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { ActivatedRoute } from '@angular/router'; -import { RouterTestingModule } from '@angular/router/testing'; -import { of } from 'rxjs'; +import { of, Subject } from 'rxjs'; +import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import { sampleWithRequiredData } from '../weight.test-samples'; import { WeightService } from '../service/weight.service'; import { WeightComponent } from './weight.component'; @@ -18,8 +19,7 @@ describe('Weight Management Component', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [RouterTestingModule.withRoutes([{ path: 'weight', component: WeightComponent }]), HttpClientTestingModule], - declarations: [WeightComponent], + imports: [HttpClientTestingModule, WeightComponent], providers: [ { provide: ActivatedRoute, @@ -34,7 +34,14 @@ describe('Weight Management Component', () => { sort: 'id,desc', }), ), - snapshot: { queryParams: {} }, + snapshot: { + queryParams: {}, + queryParamMap: jest.requireActual('@angular/router').convertToParamMap({ + page: '1', + size: '1', + sort: 'id,desc', + }), + }, }, }, ], @@ -47,15 +54,28 @@ describe('Weight Management Component', () => { service = TestBed.inject(WeightService); routerNavigateSpy = jest.spyOn(comp.router, 'navigate'); - const headers = new HttpHeaders(); - jest.spyOn(service, 'query').mockReturnValue( - of( - new HttpResponse({ - body: [{ id: 123 }], - headers, - }), - ), - ); + jest + .spyOn(service, 'query') + .mockReturnValueOnce( + of( + new HttpResponse({ + body: [{ id: 123 }], + headers: new HttpHeaders({ + link: '; rel="next"', + }), + }), + ), + ) + .mockReturnValueOnce( + of( + new HttpResponse({ + body: [{ id: 456 }], + headers: new HttpHeaders({ + link: '; rel="prev",; rel="next"', + }), + }), + ), + ); }); it('Should call load all on init', () => { @@ -77,12 +97,19 @@ describe('Weight Management Component', () => { }); }); - it('should load a page', () => { + it('should calculate the sort attribute for a non-id attribute', () => { // WHEN - comp.navigateToPage(1); + comp.navigateToWithComponentValues({ predicate: 'non-existing-column', order: 'asc' }); // THEN - expect(routerNavigateSpy).toHaveBeenCalled(); + expect(routerNavigateSpy).toHaveBeenLastCalledWith( + expect.anything(), + expect.objectContaining({ + queryParams: expect.objectContaining({ + sort: ['non-existing-column,asc'], + }), + }), + ); }); it('should calculate the sort attribute for an id', () => { @@ -93,32 +120,62 @@ describe('Weight Management Component', () => { expect(service.query).toHaveBeenLastCalledWith(expect.objectContaining({ sort: ['id,desc'] })); }); - it('should calculate the sort attribute for a non-id attribute', () => { + it('should infinite scroll', () => { // GIVEN - comp.predicate = 'name'; - - // WHEN - comp.navigateToWithComponentValues(); + comp.loadNextPage(); + comp.loadNextPage(); + comp.loadNextPage(); // THEN - expect(routerNavigateSpy).toHaveBeenLastCalledWith( - expect.anything(), - expect.objectContaining({ - queryParams: expect.objectContaining({ - sort: ['name,asc'], - }), - }), - ); + expect(service.query).toHaveBeenCalledTimes(3); + expect(service.query).toHaveBeenNthCalledWith(2, expect.objectContaining({ page: '1' })); + expect(service.query).toHaveBeenLastCalledWith(expect.objectContaining({ page: '2' })); }); - it('should re-initialize the page', () => { - // WHEN - comp.loadPage(1); - comp.reset(); + describe('delete', () => { + let ngbModal: NgbModal; + let deleteModalMock: any; - // THEN - expect(comp.page).toEqual(1); - expect(service.query).toHaveBeenCalledTimes(2); - expect(comp.weights?.[0]).toEqual(expect.objectContaining({ id: 123 })); + beforeEach(() => { + deleteModalMock = { componentInstance: {}, closed: new Subject() }; + // NgbModal is not a singleton using TestBed.inject. + // ngbModal = TestBed.inject(NgbModal); + ngbModal = (comp as any).modalService; + jest.spyOn(ngbModal, 'open').mockReturnValue(deleteModalMock); + }); + + it('on confirm should call load', inject( + [], + fakeAsync(() => { + // GIVEN + jest.spyOn(comp, 'load'); + + // WHEN + comp.delete(sampleWithRequiredData); + deleteModalMock.closed.next('deleted'); + tick(); + + // THEN + expect(ngbModal.open).toHaveBeenCalled(); + expect(comp.load).toHaveBeenCalled(); + }), + )); + + it('on dismiss should call load', inject( + [], + fakeAsync(() => { + // GIVEN + jest.spyOn(comp, 'load'); + + // WHEN + comp.delete(sampleWithRequiredData); + deleteModalMock.closed.next(); + tick(); + + // THEN + expect(ngbModal.open).toHaveBeenCalled(); + expect(comp.load).not.toHaveBeenCalled(); + }), + )); }); }); diff --git a/src/main/webapp/app/entities/weight/list/weight.component.ts b/src/main/webapp/app/entities/weight/list/weight.component.ts index e210c989..2228dec9 100644 --- a/src/main/webapp/app/entities/weight/list/weight.component.ts +++ b/src/main/webapp/app/entities/weight/list/weight.component.ts @@ -1,64 +1,86 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, computed, NgZone, inject, OnInit, signal, WritableSignal } from '@angular/core'; import { HttpHeaders } from '@angular/common/http'; -import { ActivatedRoute, Data, ParamMap, Router } from '@angular/router'; -import { combineLatest, filter, Observable, switchMap, tap } from 'rxjs'; +import { ActivatedRoute, Data, ParamMap, Router, RouterModule } from '@angular/router'; +import { combineLatest, filter, Observable, Subscription, tap } from 'rxjs'; import { NgbModal } from '@ng-bootstrap/ng-bootstrap'; +import SharedModule from 'app/shared/shared.module'; +import { sortStateSignal, SortDirective, SortByDirective, type SortState, SortService } from 'app/shared/sort'; +import { DurationPipe, FormatMediumDatetimePipe, FormatMediumDatePipe } from 'app/shared/date'; +import { FormsModule } from '@angular/forms'; + import { ITEMS_PER_PAGE } from 'app/config/pagination.constants'; -import { ASC, DESC, SORT, ITEM_DELETED_EVENT, DEFAULT_SORT_DATA } from 'app/config/navigation.constants'; +import { SORT, ITEM_DELETED_EVENT, DEFAULT_SORT_DATA } from 'app/config/navigation.constants'; import { ParseLinks } from 'app/core/util/parse-links.service'; -import { IWeight } from '../weight.model'; - +import { InfiniteScrollModule } from 'ngx-infinite-scroll'; import { EntityArrayResponseType, WeightService } from '../service/weight.service'; import { WeightDeleteDialogComponent } from '../delete/weight-delete-dialog.component'; +import { IWeight } from '../weight.model'; @Component({ + standalone: true, selector: 'jhi-weight', templateUrl: './weight.component.html', + imports: [ + RouterModule, + FormsModule, + SharedModule, + SortDirective, + SortByDirective, + DurationPipe, + FormatMediumDatetimePipe, + FormatMediumDatePipe, + InfiniteScrollModule, + ], }) export class WeightComponent implements OnInit { + subscription: Subscription | null = null; weights?: IWeight[]; isLoading = false; - predicate = 'id'; - ascending = true; + sortState = sortStateSignal({}); currentSearch = ''; itemsPerPage = ITEMS_PER_PAGE; - links: { [key: string]: number } = { - last: 0, - }; - page = 1; - - constructor( - protected weightService: WeightService, - protected activatedRoute: ActivatedRoute, - public router: Router, - protected parseLinks: ParseLinks, - protected modalService: NgbModal, - ) {} + links: WritableSignal<{ [key: string]: undefined | { [key: string]: string | undefined } }> = signal({}); + hasMorePage = computed(() => !!this.links().next); + isFirstFetch = computed(() => Object.keys(this.links()).length === 0); + + public router = inject(Router); + protected weightService = inject(WeightService); + protected activatedRoute = inject(ActivatedRoute); + protected sortService = inject(SortService); + protected parseLinks = inject(ParseLinks); + protected modalService = inject(NgbModal); + protected ngZone = inject(NgZone); + + trackId = (_index: number, item: IWeight): number => this.weightService.getWeightIdentifier(item); + + ngOnInit(): void { + this.subscription = combineLatest([this.activatedRoute.queryParamMap, this.activatedRoute.data]) + .pipe( + tap(([params, data]) => this.fillComponentAttributeFromRoute(params, data)), + tap(() => this.reset()), + tap(() => this.load()), + ) + .subscribe(); + } reset(): void { - this.page = 1; this.weights = []; - this.load(); } - loadPage(page: number): void { - this.page = page; + loadNextPage(): void { this.load(); } - trackId = (_index: number, item: IWeight): number => this.weightService.getWeightIdentifier(item); - search(query: string): void { - this.page = 1; this.currentSearch = query; - this.navigateToWithComponentValues(); + this.navigateToWithComponentValues(this.sortState()); } - ngOnInit(): void { - this.load(); + loadDefaultSortState(): void { + this.sortState.set(this.sortService.parseSortParam(this.activatedRoute.snapshot.data[DEFAULT_SORT_DATA])); } delete(weight: IWeight): void { @@ -68,42 +90,25 @@ export class WeightComponent implements OnInit { modalRef.closed .pipe( filter(reason => reason === ITEM_DELETED_EVENT), - switchMap(() => this.loadFromBackendWithRouteInformations()), + tap(() => this.load()), ) - .subscribe({ - next: (res: EntityArrayResponseType) => { - this.onResponseSuccess(res); - }, - }); + .subscribe(); } load(): void { - this.loadFromBackendWithRouteInformations().subscribe({ + this.queryBackend().subscribe({ next: (res: EntityArrayResponseType) => { this.onResponseSuccess(res); }, }); } - navigateToWithComponentValues(): void { - this.handleNavigation(this.page, this.predicate, this.ascending, this.currentSearch); - } - - navigateToPage(page = this.page): void { - this.handleNavigation(page, this.predicate, this.ascending, this.currentSearch); - } - - protected loadFromBackendWithRouteInformations(): Observable { - return combineLatest([this.activatedRoute.queryParamMap, this.activatedRoute.data]).pipe( - tap(([params, data]) => this.fillComponentAttributeFromRoute(params, data)), - switchMap(() => this.queryBackend(this.page, this.predicate, this.ascending, this.currentSearch)), - ); + navigateToWithComponentValues(event: SortState): void { + this.handleNavigation(event, this.currentSearch); } protected fillComponentAttributeFromRoute(params: ParamMap, data: Data): void { - const sort = (params.get(SORT) ?? data[DEFAULT_SORT_DATA]).split(','); - this.predicate = sort[0]; - this.ascending = sort[1] === ASC; + this.sortState.set(this.sortService.parseSortParam(params.get(SORT) ?? data[DEFAULT_SORT_DATA])); if (params.has('search') && params.get('search') !== '') { this.currentSearch = params.get('search') as string; } @@ -116,43 +121,45 @@ export class WeightComponent implements OnInit { } protected fillComponentAttributesFromResponseBody(data: IWeight[] | null): IWeight[] { - const weightsNew = this.weights ?? []; - if (data) { - for (const d of data) { - if (weightsNew.map(op => op.id).indexOf(d.id) === -1) { - weightsNew.push(d); + // If there is previous link, data is a infinite scroll pagination content. + if (this.links().prev) { + const weightsNew = this.weights ?? []; + if (data) { + for (const d of data) { + if (weightsNew.map(op => op.id).indexOf(d.id) === -1) { + weightsNew.push(d); + } } } + return weightsNew; } - return weightsNew; + return data ?? []; } protected fillComponentAttributesFromResponseHeader(headers: HttpHeaders): void { const linkHeader = headers.get('link'); if (linkHeader) { - this.links = this.parseLinks.parse(linkHeader); + this.links.set(this.parseLinks.parseAll(linkHeader)); } else { - this.links = { - last: 0, - }; + this.links.set({}); } } - protected queryBackend( - page?: number, - predicate?: string, - ascending?: boolean, - currentSearch?: string, - ): Observable { + protected queryBackend(): Observable { + const { currentSearch } = this; + this.isLoading = true; - const pageToLoad: number = page ?? 1; const queryObject: any = { - page: pageToLoad - 1, size: this.itemsPerPage, eagerload: true, query: currentSearch, - sort: this.getSortQueryParam(predicate, ascending), }; + if (this.hasMorePage()) { + Object.assign(queryObject, this.links().next); + } else if (this.isFirstFetch()) { + Object.assign(queryObject, { sort: this.sortService.buildSortParam(this.sortState()) }); + } + if (this.currentSearch && this.currentSearch !== '') { return this.weightService.search(queryObject).pipe(tap(() => (this.isLoading = false))); } else { @@ -160,26 +167,19 @@ export class WeightComponent implements OnInit { } } - protected handleNavigation(page = this.page, predicate?: string, ascending?: boolean, currentSearch?: string): void { + protected handleNavigation(sortState: SortState, currentSearch?: string): void { + this.links.set({}); + const queryParamsObj = { search: currentSearch, - page, - size: this.itemsPerPage, - sort: this.getSortQueryParam(predicate, ascending), + sort: this.sortService.buildSortParam(sortState), }; - this.router.navigate(['./'], { - relativeTo: this.activatedRoute, - queryParams: queryParamsObj, + this.ngZone.run(() => { + this.router.navigate(['./'], { + relativeTo: this.activatedRoute, + queryParams: queryParamsObj, + }); }); } - - protected getSortQueryParam(predicate = this.predicate, ascending = this.ascending): string[] { - const ascendingQueryParam = ascending ? ASC : DESC; - if (predicate === '') { - return []; - } else { - return [predicate + ',' + ascendingQueryParam]; - } - } } diff --git a/src/main/webapp/app/entities/weight/route/weight-routing-resolve.service.spec.ts b/src/main/webapp/app/entities/weight/route/weight-routing-resolve.service.spec.ts index 15b89032..25a2ebca 100644 --- a/src/main/webapp/app/entities/weight/route/weight-routing-resolve.service.spec.ts +++ b/src/main/webapp/app/entities/weight/route/weight-routing-resolve.service.spec.ts @@ -2,24 +2,22 @@ import { TestBed } from '@angular/core/testing'; import { HttpResponse } from '@angular/common/http'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { ActivatedRouteSnapshot, ActivatedRoute, Router, convertToParamMap } from '@angular/router'; -import { RouterTestingModule } from '@angular/router/testing'; import { of } from 'rxjs'; import { IWeight } from '../weight.model'; import { WeightService } from '../service/weight.service'; -import { WeightRoutingResolveService } from './weight-routing-resolve.service'; +import weightResolve from './weight-routing-resolve.service'; describe('Weight routing resolve service', () => { let mockRouter: Router; let mockActivatedRouteSnapshot: ActivatedRouteSnapshot; - let routingResolveService: WeightRoutingResolveService; let service: WeightService; let resultWeight: IWeight | null | undefined; beforeEach(() => { TestBed.configureTestingModule({ - imports: [HttpClientTestingModule, RouterTestingModule.withRoutes([])], + imports: [HttpClientTestingModule], providers: [ { provide: ActivatedRoute, @@ -34,7 +32,6 @@ describe('Weight routing resolve service', () => { mockRouter = TestBed.inject(Router); jest.spyOn(mockRouter, 'navigate').mockImplementation(() => Promise.resolve(true)); mockActivatedRouteSnapshot = TestBed.inject(ActivatedRoute).snapshot; - routingResolveService = TestBed.inject(WeightRoutingResolveService); service = TestBed.inject(WeightService); resultWeight = undefined; }); @@ -46,8 +43,12 @@ describe('Weight routing resolve service', () => { mockActivatedRouteSnapshot.params = { id: 123 }; // WHEN - routingResolveService.resolve(mockActivatedRouteSnapshot).subscribe(result => { - resultWeight = result; + TestBed.runInInjectionContext(() => { + weightResolve(mockActivatedRouteSnapshot).subscribe({ + next(result) { + resultWeight = result; + }, + }); }); // THEN @@ -61,8 +62,12 @@ describe('Weight routing resolve service', () => { mockActivatedRouteSnapshot.params = {}; // WHEN - routingResolveService.resolve(mockActivatedRouteSnapshot).subscribe(result => { - resultWeight = result; + TestBed.runInInjectionContext(() => { + weightResolve(mockActivatedRouteSnapshot).subscribe({ + next(result) { + resultWeight = result; + }, + }); }); // THEN @@ -76,8 +81,12 @@ describe('Weight routing resolve service', () => { mockActivatedRouteSnapshot.params = { id: 123 }; // WHEN - routingResolveService.resolve(mockActivatedRouteSnapshot).subscribe(result => { - resultWeight = result; + TestBed.runInInjectionContext(() => { + weightResolve(mockActivatedRouteSnapshot).subscribe({ + next(result) { + resultWeight = result; + }, + }); }); // THEN diff --git a/src/main/webapp/app/entities/weight/route/weight-routing-resolve.service.ts b/src/main/webapp/app/entities/weight/route/weight-routing-resolve.service.ts index 4ffe9088..6cd7c77a 100644 --- a/src/main/webapp/app/entities/weight/route/weight-routing-resolve.service.ts +++ b/src/main/webapp/app/entities/weight/route/weight-routing-resolve.service.ts @@ -1,33 +1,29 @@ -import { Injectable } from '@angular/core'; +import { inject } from '@angular/core'; import { HttpResponse } from '@angular/common/http'; -import { Resolve, ActivatedRouteSnapshot, Router } from '@angular/router'; -import { Observable, of, EMPTY } from 'rxjs'; +import { ActivatedRouteSnapshot, Router } from '@angular/router'; +import { of, EMPTY, Observable } from 'rxjs'; import { mergeMap } from 'rxjs/operators'; import { IWeight } from '../weight.model'; import { WeightService } from '../service/weight.service'; -@Injectable({ providedIn: 'root' }) -export class WeightRoutingResolveService implements Resolve { - constructor( - protected service: WeightService, - protected router: Router, - ) {} - - resolve(route: ActivatedRouteSnapshot): Observable { - const id = route.params['id']; - if (id) { - return this.service.find(id).pipe( +const weightResolve = (route: ActivatedRouteSnapshot): Observable => { + const id = route.params['id']; + if (id) { + return inject(WeightService) + .find(id) + .pipe( mergeMap((weight: HttpResponse) => { if (weight.body) { return of(weight.body); } else { - this.router.navigate(['404']); + inject(Router).navigate(['404']); return EMPTY; } }), ); - } - return of(null); } -} + return of(null); +}; + +export default weightResolve; diff --git a/src/main/webapp/app/entities/weight/service/weight.service.spec.ts b/src/main/webapp/app/entities/weight/service/weight.service.spec.ts index a5f18d46..2e65c692 100644 --- a/src/main/webapp/app/entities/weight/service/weight.service.spec.ts +++ b/src/main/webapp/app/entities/weight/service/weight.service.spec.ts @@ -38,7 +38,6 @@ describe('Weight Service', () => { }); it('should create a Weight', () => { - // eslint-disable-next-line @typescript-eslint/no-unused-vars const weight = { ...sampleWithNewData }; const returnedFromService = { ...requireRestSample }; const expected = { ...sampleWithRequiredData }; @@ -97,6 +96,20 @@ describe('Weight Service', () => { expect(expectedResult).toBe(expected); }); + it('should handle exceptions for searching a Weight', () => { + const queryObject: any = { + page: 0, + size: 20, + query: '', + sort: [], + }; + service.search(queryObject).subscribe(() => expectedResult); + + const req = httpMock.expectOne({ method: 'GET' }); + req.flush(null, { status: 500, statusText: 'Internal Server Error' }); + expect(expectedResult).toBe(null); + }); + describe('addWeightToCollectionIfMissing', () => { it('should add a Weight to an empty array', () => { const weight: IWeight = sampleWithRequiredData; diff --git a/src/main/webapp/app/entities/weight/service/weight.service.ts b/src/main/webapp/app/entities/weight/service/weight.service.ts index 2fb323fb..b3235532 100644 --- a/src/main/webapp/app/entities/weight/service/weight.service.ts +++ b/src/main/webapp/app/entities/weight/service/weight.service.ts @@ -1,7 +1,9 @@ -import { Injectable } from '@angular/core'; +import { inject, Injectable } from '@angular/core'; import { HttpClient, HttpResponse } from '@angular/common/http'; -import { Observable } from 'rxjs'; -import { map } from 'rxjs/operators'; +import { map, Observable, asapScheduler, scheduled } from 'rxjs'; + +import { catchError } from 'rxjs/operators'; + import dayjs from 'dayjs/esm'; import { isPresent } from 'app/core/util/operators'; @@ -27,13 +29,11 @@ export type EntityArrayResponseType = HttpResponse; @Injectable({ providedIn: 'root' }) export class WeightService { - protected resourceUrl = this.applicationConfigService.getEndpointFor('api/weights'); - protected resourceSearchUrl = this.applicationConfigService.getEndpointFor('api/_search/weights'); + protected http = inject(HttpClient); + protected applicationConfigService = inject(ApplicationConfigService); - constructor( - protected http: HttpClient, - protected applicationConfigService: ApplicationConfigService, - ) {} + protected resourceUrl = this.applicationConfigService.getEndpointFor('api/weights'); + protected resourceSearchUrl = this.applicationConfigService.getEndpointFor('api/weights/_search'); create(weight: NewWeight): Observable { const copy = this.convertDateFromClient(weight); @@ -75,9 +75,11 @@ export class WeightService { search(req: SearchWithPagination): Observable { const options = createRequestOption(req); - return this.http - .get(this.resourceSearchUrl, { params: options, observe: 'response' }) - .pipe(map(res => this.convertResponseArrayFromServer(res))); + return this.http.get(this.resourceSearchUrl, { params: options, observe: 'response' }).pipe( + map(res => this.convertResponseArrayFromServer(res)), + + catchError(() => scheduled([new HttpResponse()], asapScheduler)), + ); } getWeightIdentifier(weight: Pick): number { @@ -94,7 +96,7 @@ export class WeightService { ): Type[] { const weights: Type[] = weightsToCheck.filter(isPresent); if (weights.length > 0) { - const weightCollectionIdentifiers = weightCollection.map(weightItem => this.getWeightIdentifier(weightItem)!); + const weightCollectionIdentifiers = weightCollection.map(weightItem => this.getWeightIdentifier(weightItem)); const weightsToAdd = weights.filter(weightItem => { const weightIdentifier = this.getWeightIdentifier(weightItem); if (weightCollectionIdentifiers.includes(weightIdentifier)) { diff --git a/src/main/webapp/app/entities/weight/update/weight-form.service.spec.ts b/src/main/webapp/app/entities/weight/update/weight-form.service.spec.ts index ac26bf9a..5e301b1c 100644 --- a/src/main/webapp/app/entities/weight/update/weight-form.service.spec.ts +++ b/src/main/webapp/app/entities/weight/update/weight-form.service.spec.ts @@ -43,7 +43,6 @@ describe('Weight Form Service', () => { describe('getWeight', () => { it('should return NewWeight for default Weight initial value', () => { - // eslint-disable-next-line @typescript-eslint/no-unused-vars const formGroup = service.createWeightFormGroup(sampleWithNewData); const weight = service.getWeight(formGroup) as any; diff --git a/src/main/webapp/app/entities/weight/update/weight-update.component.html b/src/main/webapp/app/entities/weight/update/weight-update.component.html index c2209544..df49069f 100644 --- a/src/main/webapp/app/entities/weight/update/weight-update.component.html +++ b/src/main/webapp/app/entities/weight/update/weight-update.component.html @@ -1,6 +1,6 @@
-
+

Create or edit a Weight

@@ -8,13 +8,15 @@

- - -

+ @if (editForm.controls.id.value !== null) { +
+ + +
+ } -
- +
+
-
- - This field is required. - - - This field should be a date and time. - -
+ @if (editForm.get('timestamp')!.invalid && (editForm.get('timestamp')!.dirty || editForm.get('timestamp')!.touched)) { +
+ @if (editForm.get('timestamp')?.errors?.required) { + This field is required. + } + This field should be a date and time. +
+ }
-
- +
+ -
- - This field is required. - - - This field should be a number. - -
+ @if (editForm.get('weight')!.invalid && (editForm.get('weight')!.dirty || editForm.get('weight')!.touched)) { +
+ @if (editForm.get('weight')?.errors?.required) { + This field is required. + } + This field should be a number. +
+ }
-
- +
+
diff --git a/src/main/webapp/app/entities/weight/update/weight-update.component.spec.ts b/src/main/webapp/app/entities/weight/update/weight-update.component.spec.ts index 098ce481..47fac15f 100644 --- a/src/main/webapp/app/entities/weight/update/weight-update.component.spec.ts +++ b/src/main/webapp/app/entities/weight/update/weight-update.component.spec.ts @@ -3,14 +3,12 @@ import { HttpResponse } from '@angular/common/http'; import { HttpClientTestingModule } from '@angular/common/http/testing'; import { FormBuilder } from '@angular/forms'; import { ActivatedRoute } from '@angular/router'; -import { RouterTestingModule } from '@angular/router/testing'; import { of, Subject, from } from 'rxjs'; import { IUser } from 'app/entities/user/user.model'; -import { UserService } from 'app/entities/user/user.service'; +import { UserService } from 'app/entities/user/service/user.service'; import { WeightService } from '../service/weight.service'; import { IWeight } from '../weight.model'; - import { WeightFormService } from './weight-form.service'; import { WeightUpdateComponent } from './weight-update.component'; @@ -25,8 +23,7 @@ describe('Weight Management Update Component', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [HttpClientTestingModule, RouterTestingModule.withRoutes([])], - declarations: [WeightUpdateComponent], + imports: [HttpClientTestingModule, WeightUpdateComponent], providers: [ FormBuilder, { @@ -52,10 +49,10 @@ describe('Weight Management Update Component', () => { describe('ngOnInit', () => { it('Should call User query and add missing value', () => { const weight: IWeight = { id: 456 }; - const user: IUser = { id: 19261 }; + const user: IUser = { id: 25832 }; weight.user = user; - const userCollection: IUser[] = [{ id: 48569 }]; + const userCollection: IUser[] = [{ id: 23694 }]; jest.spyOn(userService, 'query').mockReturnValue(of(new HttpResponse({ body: userCollection }))); const additionalUsers = [user]; const expectedCollection: IUser[] = [...additionalUsers, ...userCollection]; @@ -74,7 +71,7 @@ describe('Weight Management Update Component', () => { it('Should update editForm', () => { const weight: IWeight = { id: 456 }; - const user: IUser = { id: 30575 }; + const user: IUser = { id: 23506 }; weight.user = user; activatedRoute.data = of({ weight }); diff --git a/src/main/webapp/app/entities/weight/update/weight-update.component.ts b/src/main/webapp/app/entities/weight/update/weight-update.component.ts index 481ec893..4a88e317 100644 --- a/src/main/webapp/app/entities/weight/update/weight-update.component.ts +++ b/src/main/webapp/app/entities/weight/update/weight-update.component.ts @@ -1,18 +1,23 @@ -import { Component, OnInit } from '@angular/core'; +import { Component, inject, OnInit } from '@angular/core'; import { HttpResponse } from '@angular/common/http'; import { ActivatedRoute } from '@angular/router'; import { Observable } from 'rxjs'; import { finalize, map } from 'rxjs/operators'; +import SharedModule from 'app/shared/shared.module'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; + import { IUser } from 'app/entities/user/user.model'; -import { UserService } from 'app/entities/user/user.service'; +import { UserService } from 'app/entities/user/service/user.service'; import { IWeight } from '../weight.model'; import { WeightService } from '../service/weight.service'; import { WeightFormService, WeightFormGroup } from './weight-form.service'; @Component({ + standalone: true, selector: 'jhi-weight-update', templateUrl: './weight-update.component.html', + imports: [SharedModule, FormsModule, ReactiveFormsModule], }) export class WeightUpdateComponent implements OnInit { isSaving = false; @@ -20,14 +25,13 @@ export class WeightUpdateComponent implements OnInit { usersSharedCollection: IUser[] = []; - editForm: WeightFormGroup = this.weightFormService.createWeightFormGroup(); + protected weightService = inject(WeightService); + protected weightFormService = inject(WeightFormService); + protected userService = inject(UserService); + protected activatedRoute = inject(ActivatedRoute); - constructor( - protected weightService: WeightService, - protected weightFormService: WeightFormService, - protected userService: UserService, - protected activatedRoute: ActivatedRoute, - ) {} + // eslint-disable-next-line @typescript-eslint/member-ordering + editForm: WeightFormGroup = this.weightFormService.createWeightFormGroup(); compareUser = (o1: IUser | null, o2: IUser | null): boolean => this.userService.compareUser(o1, o2); diff --git a/src/main/webapp/app/entities/weight/weight.module.ts b/src/main/webapp/app/entities/weight/weight.module.ts deleted file mode 100644 index e086e7d0..00000000 --- a/src/main/webapp/app/entities/weight/weight.module.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { NgModule } from '@angular/core'; -import { SharedModule } from 'app/shared/shared.module'; -import { WeightComponent } from './list/weight.component'; -import { WeightDetailComponent } from './detail/weight-detail.component'; -import { WeightUpdateComponent } from './update/weight-update.component'; -import { WeightDeleteDialogComponent } from './delete/weight-delete-dialog.component'; -import { WeightRoutingModule } from './route/weight-routing.module'; - -@NgModule({ - imports: [SharedModule, WeightRoutingModule], - declarations: [WeightComponent, WeightDetailComponent, WeightUpdateComponent, WeightDeleteDialogComponent], -}) -export class WeightModule {} diff --git a/src/main/webapp/app/entities/weight/route/weight-routing.module.ts b/src/main/webapp/app/entities/weight/weight.routes.ts similarity index 52% rename from src/main/webapp/app/entities/weight/route/weight-routing.module.ts rename to src/main/webapp/app/entities/weight/weight.routes.ts index 60a5ab5d..8fc9e905 100644 --- a/src/main/webapp/app/entities/weight/route/weight-routing.module.ts +++ b/src/main/webapp/app/entities/weight/weight.routes.ts @@ -1,12 +1,11 @@ -import { NgModule } from '@angular/core'; -import { RouterModule, Routes } from '@angular/router'; +import { Routes } from '@angular/router'; import { UserRouteAccessService } from 'app/core/auth/user-route-access.service'; import { ASC } from 'app/config/navigation.constants'; -import { WeightComponent } from '../list/weight.component'; -import { WeightDetailComponent } from '../detail/weight-detail.component'; -import { WeightUpdateComponent } from '../update/weight-update.component'; -import { WeightRoutingResolveService } from './weight-routing-resolve.service'; +import { WeightComponent } from './list/weight.component'; +import { WeightDetailComponent } from './detail/weight-detail.component'; +import { WeightUpdateComponent } from './update/weight-update.component'; +import WeightResolve from './route/weight-routing-resolve.service'; const weightRoute: Routes = [ { @@ -21,7 +20,7 @@ const weightRoute: Routes = [ path: ':id/view', component: WeightDetailComponent, resolve: { - weight: WeightRoutingResolveService, + weight: WeightResolve, }, canActivate: [UserRouteAccessService], }, @@ -29,7 +28,7 @@ const weightRoute: Routes = [ path: 'new', component: WeightUpdateComponent, resolve: { - weight: WeightRoutingResolveService, + weight: WeightResolve, }, canActivate: [UserRouteAccessService], }, @@ -37,14 +36,10 @@ const weightRoute: Routes = [ path: ':id/edit', component: WeightUpdateComponent, resolve: { - weight: WeightRoutingResolveService, + weight: WeightResolve, }, canActivate: [UserRouteAccessService], }, ]; -@NgModule({ - imports: [RouterModule.forChild(weightRoute)], - exports: [RouterModule], -}) -export class WeightRoutingModule {} +export default weightRoute; diff --git a/src/main/webapp/app/entities/weight/weight.test-samples.ts b/src/main/webapp/app/entities/weight/weight.test-samples.ts index b46702b9..606d1b56 100644 --- a/src/main/webapp/app/entities/weight/weight.test-samples.ts +++ b/src/main/webapp/app/entities/weight/weight.test-samples.ts @@ -3,26 +3,26 @@ import dayjs from 'dayjs/esm'; import { IWeight, NewWeight } from './weight.model'; export const sampleWithRequiredData: IWeight = { - id: 86192, - timestamp: dayjs('2022-11-07T08:31'), - weight: 42379, + id: 4890, + timestamp: dayjs('2022-11-07T07:25'), + weight: 24215.41, }; export const sampleWithPartialData: IWeight = { - id: 53022, - timestamp: dayjs('2022-11-07T07:54'), - weight: 24033, + id: 1135, + timestamp: dayjs('2022-11-07T23:09'), + weight: 17356.05, }; export const sampleWithFullData: IWeight = { - id: 69421, - timestamp: dayjs('2022-11-07T10:51'), - weight: 98529, + id: 603, + timestamp: dayjs('2022-11-07T10:41'), + weight: 25306.98, }; export const sampleWithNewData: NewWeight = { - timestamp: dayjs('2022-11-07T02:56'), - weight: 7905, + timestamp: dayjs('2022-11-07T17:04'), + weight: 28535.16, id: null, }; diff --git a/src/main/webapp/app/home/home.component.html b/src/main/webapp/app/home/home.component.html index 2a9ed8d7..f56bae2a 100644 --- a/src/main/webapp/app/home/home.component.html +++ b/src/main/webapp/app/home/home.component.html @@ -8,26 +8,30 @@

Welcome, Java Hipster!This is your homepage

-
-
- You are logged in as user "{{ account.login }}". -
- -
- If you want to - sign in, you can try the default accounts:
- Administrator (login="admin" and password="admin")
- User (login="user" and - password="user").
-
+
+ @if (account() !== null) { +
+ @if (account()) { + You are logged in as user "{{ account()!.login }}". + } +
+ } @else { +
+ If you want to + sign in, you can try the default accounts:
- Administrator (login="admin" and password="admin")
- User + (login="user" and password="user").
+
-
- You don't have an account yet?  - Register a new account -
+
+ You don't have an account yet?  + Register a new account +
+ }

If you have any question on JHipster:

@@ -63,13 +67,13 @@

Welcome, Java Hipster!
  • follow @jhipster on Twitterfollow @jhipster on Twitter
  • - If you like JHipster, don't forget to give us a star on + If you like JHipster, don't forget to give us a star on GitHub! diff --git a/src/main/webapp/app/home/home.component.spec.ts b/src/main/webapp/app/home/home.component.spec.ts index 41d7991a..b441a138 100644 --- a/src/main/webapp/app/home/home.component.spec.ts +++ b/src/main/webapp/app/home/home.component.spec.ts @@ -2,13 +2,12 @@ jest.mock('app/core/auth/account.service'); import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; import { Router } from '@angular/router'; -import { RouterTestingModule } from '@angular/router/testing'; import { of, Subject } from 'rxjs'; import { AccountService } from 'app/core/auth/account.service'; import { Account } from 'app/core/auth/account.model'; -import { HomeComponent } from './home.component'; +import HomeComponent from './home.component'; describe('Home Component', () => { let comp: HomeComponent; @@ -28,8 +27,7 @@ describe('Home Component', () => { beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ - imports: [RouterTestingModule.withRoutes([])], - declarations: [HomeComponent], + imports: [HomeComponent], providers: [AccountService], }) .overrideTemplate(HomeComponent, '') @@ -57,19 +55,19 @@ describe('Home Component', () => { comp.ngOnInit(); // THEN - expect(comp.account).toBeNull(); + expect(comp.account()).toBeNull(); // WHEN authenticationState.next(account); // THEN - expect(comp.account).toEqual(account); + expect(comp.account()).toEqual(account); // WHEN authenticationState.next(null); // THEN - expect(comp.account).toBeNull(); + expect(comp.account()).toBeNull(); }); }); @@ -93,20 +91,20 @@ describe('Home Component', () => { comp.ngOnInit(); // THEN - expect(comp.account).toBeNull(); + expect(comp.account()).toBeNull(); // WHEN authenticationState.next(account); // THEN - expect(comp.account).toEqual(account); + expect(comp.account()).toEqual(account); // WHEN comp.ngOnDestroy(); authenticationState.next(null); // THEN - expect(comp.account).toEqual(account); + expect(comp.account()).toEqual(account); }); }); }); diff --git a/src/main/webapp/app/home/home.component.ts b/src/main/webapp/app/home/home.component.ts index 04d6df59..d0f05221 100644 --- a/src/main/webapp/app/home/home.component.ts +++ b/src/main/webapp/app/home/home.component.ts @@ -1,31 +1,32 @@ -import { Component, OnInit, OnDestroy } from '@angular/core'; -import { Router } from '@angular/router'; +import { Component, inject, signal, OnInit, OnDestroy } from '@angular/core'; +import { Router, RouterModule } from '@angular/router'; import { Subject } from 'rxjs'; import { takeUntil } from 'rxjs/operators'; +import SharedModule from 'app/shared/shared.module'; import { AccountService } from 'app/core/auth/account.service'; import { Account } from 'app/core/auth/account.model'; @Component({ + standalone: true, selector: 'jhi-home', templateUrl: './home.component.html', - styleUrls: ['./home.component.scss'], + styleUrl: './home.component.scss', + imports: [SharedModule, RouterModule], }) -export class HomeComponent implements OnInit, OnDestroy { - account: Account | null = null; +export default class HomeComponent implements OnInit, OnDestroy { + account = signal(null); private readonly destroy$ = new Subject(); - constructor( - private accountService: AccountService, - private router: Router, - ) {} + private accountService = inject(AccountService); + private router = inject(Router); ngOnInit(): void { this.accountService .getAuthenticationState() .pipe(takeUntil(this.destroy$)) - .subscribe(account => (this.account = account)); + .subscribe(account => this.account.set(account)); } login(): void { diff --git a/src/main/webapp/app/home/home.module.ts b/src/main/webapp/app/home/home.module.ts deleted file mode 100644 index 758f5c48..00000000 --- a/src/main/webapp/app/home/home.module.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { NgModule } from '@angular/core'; -import { RouterModule } from '@angular/router'; - -import { SharedModule } from 'app/shared/shared.module'; -import { HOME_ROUTE } from './home.route'; -import { HomeComponent } from './home.component'; - -@NgModule({ - imports: [SharedModule, RouterModule.forChild([HOME_ROUTE])], - declarations: [HomeComponent], -}) -export class HomeModule {} diff --git a/src/main/webapp/app/home/home.route.ts b/src/main/webapp/app/home/home.route.ts deleted file mode 100644 index 213002d1..00000000 --- a/src/main/webapp/app/home/home.route.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { Route } from '@angular/router'; - -import { HomeComponent } from './home.component'; - -export const HOME_ROUTE: Route = { - path: '', - component: HomeComponent, - data: { - pageTitle: 'home.title', - }, -}; diff --git a/src/main/webapp/app/layouts/error/error.component.html b/src/main/webapp/app/layouts/error/error.component.html index c1b52a55..d10967e9 100644 --- a/src/main/webapp/app/layouts/error/error.component.html +++ b/src/main/webapp/app/layouts/error/error.component.html @@ -7,9 +7,9 @@

    Error page!

    -
    -
    {{ errorMessage }}
    -
    + @if (errorMessage()) { +
    {{ errorMessage() }}
    + }

    diff --git a/src/main/webapp/app/layouts/error/error.component.ts b/src/main/webapp/app/layouts/error/error.component.ts index db5e63d8..cb98a988 100644 --- a/src/main/webapp/app/layouts/error/error.component.ts +++ b/src/main/webapp/app/layouts/error/error.component.ts @@ -1,21 +1,22 @@ -import { Component, OnInit, OnDestroy } from '@angular/core'; +import { Component, inject, signal, OnInit, OnDestroy } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Subscription } from 'rxjs'; import { TranslateService } from '@ngx-translate/core'; +import SharedModule from 'app/shared/shared.module'; @Component({ + standalone: true, selector: 'jhi-error', templateUrl: './error.component.html', + imports: [SharedModule], }) -export class ErrorComponent implements OnInit, OnDestroy { - errorMessage?: string; +export default class ErrorComponent implements OnInit, OnDestroy { + errorMessage = signal(undefined); errorKey?: string; langChangeSubscription?: Subscription; - constructor( - private translateService: TranslateService, - private route: ActivatedRoute, - ) {} + private translateService = inject(TranslateService); + private route = inject(ActivatedRoute); ngOnInit(): void { this.route.data.subscribe(routeData => { @@ -34,10 +35,10 @@ export class ErrorComponent implements OnInit, OnDestroy { } private getErrorMessageTranslation(): void { - this.errorMessage = ''; + this.errorMessage.set(''); if (this.errorKey) { this.translateService.get(this.errorKey).subscribe(translatedErrorMessage => { - this.errorMessage = translatedErrorMessage; + this.errorMessage.set(translatedErrorMessage); }); } } diff --git a/src/main/webapp/app/layouts/error/error.route.ts b/src/main/webapp/app/layouts/error/error.route.ts index a8574019..85f911b5 100644 --- a/src/main/webapp/app/layouts/error/error.route.ts +++ b/src/main/webapp/app/layouts/error/error.route.ts @@ -1,30 +1,28 @@ import { Routes } from '@angular/router'; -import { ErrorComponent } from './error.component'; +import ErrorComponent from './error.component'; export const errorRoute: Routes = [ { path: 'error', component: ErrorComponent, - data: { - pageTitle: 'error.title', - }, + title: 'error.title', }, { path: 'accessdenied', component: ErrorComponent, data: { - pageTitle: 'error.title', errorMessage: 'error.http.403', }, + title: 'error.title', }, { path: '404', component: ErrorComponent, data: { - pageTitle: 'error.title', errorMessage: 'error.http.404', }, + title: 'error.title', }, { path: '**', diff --git a/src/main/webapp/app/layouts/footer/footer.component.ts b/src/main/webapp/app/layouts/footer/footer.component.ts index 7c640ec8..9326a71b 100644 --- a/src/main/webapp/app/layouts/footer/footer.component.ts +++ b/src/main/webapp/app/layouts/footer/footer.component.ts @@ -1,7 +1,10 @@ import { Component } from '@angular/core'; +import { TranslateDirective } from 'app/shared/language'; @Component({ + standalone: true, selector: 'jhi-footer', templateUrl: './footer.component.html', + imports: [TranslateDirective], }) -export class FooterComponent {} +export default class FooterComponent {} diff --git a/src/main/webapp/app/layouts/main/main.component.spec.ts b/src/main/webapp/app/layouts/main/main.component.spec.ts index 4e137b5c..34b41476 100644 --- a/src/main/webapp/app/layouts/main/main.component.spec.ts +++ b/src/main/webapp/app/layouts/main/main.component.spec.ts @@ -1,14 +1,17 @@ jest.mock('app/core/auth/account.service'); -import { waitForAsync, ComponentFixture, TestBed } from '@angular/core/testing'; -import { Router, RouterEvent, NavigationEnd, NavigationStart } from '@angular/router'; +import { waitForAsync, ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; +import { Router, TitleStrategy } from '@angular/router'; import { Title } from '@angular/platform-browser'; -import { Subject, of } from 'rxjs'; +import { DOCUMENT } from '@angular/common'; +import { Component, NgZone } from '@angular/core'; +import { of } from 'rxjs'; import { TranslateModule, TranslateService, LangChangeEvent } from '@ngx-translate/core'; import { AccountService } from 'app/core/auth/account.service'; -import { MainComponent } from './main.component'; +import { AppPageTitleStrategy } from 'app/app-page-title-strategy'; +import MainComponent from './main.component'; describe('MainComponent', () => { let comp: MainComponent; @@ -16,25 +19,15 @@ describe('MainComponent', () => { let titleService: Title; let translateService: TranslateService; let mockAccountService: AccountService; - const routerEventsSubject = new Subject(); + let ngZone: NgZone; const routerState: any = { snapshot: { root: { data: {} } } }; - class MockRouter { - events = routerEventsSubject; - routerState = routerState; - } + let router: Router; + let document: Document; beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ - imports: [TranslateModule.forRoot()], - declarations: [MainComponent], - providers: [ - Title, - AccountService, - { - provide: Router, - useClass: MockRouter, - }, - ], + imports: [TranslateModule.forRoot(), MainComponent], + providers: [Title, AccountService, { provide: TitleStrategy, useClass: AppPageTitleStrategy }], }) .overrideTemplate(MainComponent, '') .compileComponents(); @@ -48,14 +41,15 @@ describe('MainComponent', () => { mockAccountService = TestBed.inject(AccountService); mockAccountService.identity = jest.fn(() => of(null)); mockAccountService.getAuthenticationState = jest.fn(() => of(null)); + ngZone = TestBed.inject(NgZone); + router = TestBed.inject(Router); + document = TestBed.inject(DOCUMENT); }); describe('page title', () => { const defaultPageTitle = 'global.title'; const parentRoutePageTitle = 'parentTitle'; const childRoutePageTitle = 'childTitle'; - const navigationEnd = new NavigationEnd(1, '', ''); - const navigationStart = new NavigationStart(1, ''); const langChangeEvent: LangChangeEvent = { lang: 'en', translations: null }; beforeEach(() => { @@ -67,62 +61,62 @@ describe('MainComponent', () => { }); describe('navigation end', () => { - it('should set page title to default title if pageTitle is missing on routes', () => { + it('should set page title to default title if pageTitle is missing on routes', fakeAsync(() => { // WHEN - routerEventsSubject.next(navigationEnd); + ngZone.run(() => router.navigateByUrl('')); + tick(); // THEN - expect(translateService.get).toHaveBeenCalledWith(defaultPageTitle); - expect(titleService.setTitle).toHaveBeenCalledWith(defaultPageTitle + ' translated'); - }); + expect(document.title).toBe(defaultPageTitle + ' translated'); + })); - it('should set page title to root route pageTitle if there is no child routes', () => { + it('should set page title to root route pageTitle if there is no child routes', fakeAsync(() => { // GIVEN - routerState.snapshot.root.data = { pageTitle: parentRoutePageTitle }; + router.resetConfig([{ path: '', title: parentRoutePageTitle, component: BlankComponent }]); // WHEN - routerEventsSubject.next(navigationEnd); + ngZone.run(() => router.navigateByUrl('')); + tick(); // THEN - expect(translateService.get).toHaveBeenCalledWith(parentRoutePageTitle); - expect(titleService.setTitle).toHaveBeenCalledWith(parentRoutePageTitle + ' translated'); - }); + expect(document.title).toBe(parentRoutePageTitle + ' translated'); + })); - it('should set page title to child route pageTitle if child routes exist and pageTitle is set for child route', () => { + it('should set page title to child route pageTitle if child routes exist and pageTitle is set for child route', fakeAsync(() => { // GIVEN - routerState.snapshot.root.data = { pageTitle: parentRoutePageTitle }; - routerState.snapshot.root.firstChild = { data: { pageTitle: childRoutePageTitle } }; + router.resetConfig([ + { + path: 'home', + title: parentRoutePageTitle, + children: [{ path: '', title: childRoutePageTitle, component: BlankComponent }], + }, + ]); // WHEN - routerEventsSubject.next(navigationEnd); + ngZone.run(() => router.navigateByUrl('home')); + tick(); // THEN - expect(translateService.get).toHaveBeenCalledWith(childRoutePageTitle); - expect(titleService.setTitle).toHaveBeenCalledWith(childRoutePageTitle + ' translated'); - }); + expect(document.title).toBe(childRoutePageTitle + ' translated'); + })); - it('should set page title to parent route pageTitle if child routes exists but pageTitle is not set for child route data', () => { + it('should set page title to parent route pageTitle if child routes exists but pageTitle is not set for child route data', fakeAsync(() => { // GIVEN - routerState.snapshot.root.data = { pageTitle: parentRoutePageTitle }; - routerState.snapshot.root.firstChild = { data: {} }; + router.resetConfig([ + { + path: 'home', + title: parentRoutePageTitle, + children: [{ path: '', component: BlankComponent }], + }, + ]); // WHEN - routerEventsSubject.next(navigationEnd); + ngZone.run(() => router.navigateByUrl('home')); + tick(); // THEN - expect(translateService.get).toHaveBeenCalledWith(parentRoutePageTitle); - expect(titleService.setTitle).toHaveBeenCalledWith(parentRoutePageTitle + ' translated'); - }); - }); - - describe('navigation start', () => { - it('should not set page title on navigation start', () => { - // WHEN - routerEventsSubject.next(navigationStart); - - // THEN - expect(titleService.setTitle).not.toHaveBeenCalled(); - }); + expect(document.title).toBe(parentRoutePageTitle + ' translated'); + })); }); describe('language change', () => { @@ -131,47 +125,84 @@ describe('MainComponent', () => { translateService.onLangChange.emit(langChangeEvent); // THEN - expect(translateService.get).toHaveBeenCalledWith(defaultPageTitle); - expect(titleService.setTitle).toHaveBeenCalledWith(defaultPageTitle + ' translated'); + expect(document.title).toBe(defaultPageTitle + ' translated'); }); - it('should set page title to root route pageTitle if there is no child routes', () => { + it('should set page title to root route pageTitle if there is no child routes', fakeAsync(() => { // GIVEN routerState.snapshot.root.data = { pageTitle: parentRoutePageTitle }; + router.resetConfig([{ path: '', title: parentRoutePageTitle, component: BlankComponent }]); + + // WHEN + ngZone.run(() => router.navigateByUrl('')); + tick(); + + // THEN + expect(document.title).toBe(parentRoutePageTitle + ' translated'); + + // GIVEN + document.title = 'other title'; // WHEN translateService.onLangChange.emit(langChangeEvent); // THEN - expect(translateService.get).toHaveBeenCalledWith(parentRoutePageTitle); - expect(titleService.setTitle).toHaveBeenCalledWith(parentRoutePageTitle + ' translated'); - }); + expect(document.title).toBe(parentRoutePageTitle + ' translated'); + })); - it('should set page title to child route pageTitle if child routes exist and pageTitle is set for child route', () => { + it('should set page title to child route pageTitle if child routes exist and pageTitle is set for child route', fakeAsync(() => { // GIVEN - routerState.snapshot.root.data = { pageTitle: parentRoutePageTitle }; - routerState.snapshot.root.firstChild = { data: { pageTitle: childRoutePageTitle } }; + router.resetConfig([ + { + path: 'home', + title: parentRoutePageTitle, + children: [{ path: '', title: childRoutePageTitle, component: BlankComponent }], + }, + ]); + + // WHEN + ngZone.run(() => router.navigateByUrl('home')); + tick(); + + // THEN + expect(document.title).toBe(childRoutePageTitle + ' translated'); + + // GIVEN + document.title = 'other title'; // WHEN translateService.onLangChange.emit(langChangeEvent); // THEN - expect(translateService.get).toHaveBeenCalledWith(childRoutePageTitle); - expect(titleService.setTitle).toHaveBeenCalledWith(childRoutePageTitle + ' translated'); - }); + expect(document.title).toBe(childRoutePageTitle + ' translated'); + })); - it('should set page title to parent route pageTitle if child routes exists but pageTitle is not set for child route data', () => { + it('should set page title to parent route pageTitle if child routes exists but pageTitle is not set for child route data', fakeAsync(() => { // GIVEN - routerState.snapshot.root.data = { pageTitle: parentRoutePageTitle }; - routerState.snapshot.root.firstChild = { data: {} }; + router.resetConfig([ + { + path: 'home', + title: parentRoutePageTitle, + children: [{ path: '', component: BlankComponent }], + }, + ]); + + // WHEN + ngZone.run(() => router.navigateByUrl('home')); + tick(); + + // THEN + expect(document.title).toBe(parentRoutePageTitle + ' translated'); + + // GIVEN + document.title = 'other title'; // WHEN translateService.onLangChange.emit(langChangeEvent); // THEN - expect(translateService.get).toHaveBeenCalledWith(parentRoutePageTitle); - expect(titleService.setTitle).toHaveBeenCalledWith(parentRoutePageTitle + ' translated'); - }); + expect(document.title).toBe(parentRoutePageTitle + ' translated'); + })); }); }); @@ -194,3 +225,6 @@ describe('MainComponent', () => { }); }); }); + +@Component({ template: '' }) +export class BlankComponent {} diff --git a/src/main/webapp/app/layouts/main/main.component.ts b/src/main/webapp/app/layouts/main/main.component.ts index a78f1176..6d071a32 100644 --- a/src/main/webapp/app/layouts/main/main.component.ts +++ b/src/main/webapp/app/layouts/main/main.component.ts @@ -1,58 +1,41 @@ -import { Component, OnInit, RendererFactory2, Renderer2 } from '@angular/core'; -import { Title } from '@angular/platform-browser'; -import { Router, ActivatedRouteSnapshot, NavigationEnd } from '@angular/router'; +import { Component, inject, OnInit, RendererFactory2, Renderer2 } from '@angular/core'; +import { RouterOutlet, Router } from '@angular/router'; import { TranslateService, LangChangeEvent } from '@ngx-translate/core'; import dayjs from 'dayjs/esm'; import { AccountService } from 'app/core/auth/account.service'; +import { AppPageTitleStrategy } from 'app/app-page-title-strategy'; +import FooterComponent from '../footer/footer.component'; +import PageRibbonComponent from '../profiles/page-ribbon.component'; @Component({ + standalone: true, selector: 'jhi-main', templateUrl: './main.component.html', + providers: [AppPageTitleStrategy], + imports: [RouterOutlet, FooterComponent, PageRibbonComponent], }) -export class MainComponent implements OnInit { +export default class MainComponent implements OnInit { private renderer: Renderer2; - constructor( - private accountService: AccountService, - private titleService: Title, - private router: Router, - private translateService: TranslateService, - rootRenderer: RendererFactory2, - ) { - this.renderer = rootRenderer.createRenderer(document.querySelector('html'), null); + private router = inject(Router); + private appPageTitleStrategy = inject(AppPageTitleStrategy); + private accountService = inject(AccountService); + private translateService = inject(TranslateService); + private rootRenderer = inject(RendererFactory2); + + constructor() { + this.renderer = this.rootRenderer.createRenderer(document.querySelector('html'), null); } ngOnInit(): void { // try to log in automatically this.accountService.identity().subscribe(); - this.router.events.subscribe(event => { - if (event instanceof NavigationEnd) { - this.updateTitle(); - } - }); - this.translateService.onLangChange.subscribe((langChangeEvent: LangChangeEvent) => { - this.updateTitle(); + this.appPageTitleStrategy.updateTitle(this.router.routerState.snapshot); dayjs.locale(langChangeEvent.lang); this.renderer.setAttribute(document.querySelector('html'), 'lang', langChangeEvent.lang); }); } - - private getPageTitle(routeSnapshot: ActivatedRouteSnapshot): string { - const title: string = routeSnapshot.data['pageTitle'] ?? ''; - if (routeSnapshot.firstChild) { - return this.getPageTitle(routeSnapshot.firstChild) || title; - } - return title; - } - - private updateTitle(): void { - let pageTitle = this.getPageTitle(this.router.routerState.snapshot.root); - if (!pageTitle) { - pageTitle = 'global.title'; - } - this.translateService.get(pageTitle).subscribe(title => this.titleService.setTitle(title)); - } } diff --git a/src/main/webapp/app/layouts/navbar/active-menu.directive.ts b/src/main/webapp/app/layouts/navbar/active-menu.directive.ts index f34ada5e..3a95c182 100644 --- a/src/main/webapp/app/layouts/navbar/active-menu.directive.ts +++ b/src/main/webapp/app/layouts/navbar/active-menu.directive.ts @@ -1,17 +1,16 @@ -import { Directive, OnInit, ElementRef, Renderer2, Input } from '@angular/core'; +import { Directive, OnInit, ElementRef, Renderer2, inject, Input } from '@angular/core'; import { TranslateService, LangChangeEvent } from '@ngx-translate/core'; @Directive({ + standalone: true, selector: '[jhiActiveMenu]', }) -export class ActiveMenuDirective implements OnInit { +export default class ActiveMenuDirective implements OnInit { @Input() jhiActiveMenu?: string; - constructor( - private el: ElementRef, - private renderer: Renderer2, - private translateService: TranslateService, - ) {} + private el = inject(ElementRef); + private renderer = inject(Renderer2); + private translateService = inject(TranslateService); ngOnInit(): void { this.translateService.onLangChange.subscribe((event: LangChangeEvent) => { diff --git a/src/main/webapp/app/layouts/navbar/navbar-item.model.d.ts b/src/main/webapp/app/layouts/navbar/navbar-item.model.d.ts new file mode 100644 index 00000000..d6f1cca1 --- /dev/null +++ b/src/main/webapp/app/layouts/navbar/navbar-item.model.d.ts @@ -0,0 +1,7 @@ +type NavbarItem = { + name: string; + route: string; + translationKey: string; +}; + +export default NavbarItem; diff --git a/src/main/webapp/app/layouts/navbar/navbar.component.html b/src/main/webapp/app/layouts/navbar/navbar.component.html index 118b8057..d994912e 100644 --- a/src/main/webapp/app/layouts/navbar/navbar.component.html +++ b/src/main/webapp/app/layouts/navbar/navbar.component.html @@ -2,7 +2,7 @@
    -

    * Properties are configured in the {@code application.yml} file. * See {@link tech.jhipster.config.JHipsterProperties} for a good example. @@ -11,22 +11,27 @@ @ConfigurationProperties(prefix = "application", ignoreUnknownFields = false) public class ApplicationProperties { - private final Elasticsearch elasticsearch = new Elasticsearch(); + private final Liquibase liquibase = new Liquibase(); - public Elasticsearch getElasticsearch() { - return elasticsearch; + // jhipster-needle-application-properties-property + + public Liquibase getLiquibase() { + return liquibase; } - public static class Elasticsearch { + // jhipster-needle-application-properties-property-getter + + public static class Liquibase { - private boolean reindexOnStartup; + private Boolean asyncStart; - public boolean getReindexOnStartup() { - return reindexOnStartup; + public Boolean getAsyncStart() { + return asyncStart; } - public void setReindexOnStartup(boolean reindexOnStartup) { - this.reindexOnStartup = reindexOnStartup; + public void setAsyncStart(Boolean asyncStart) { + this.asyncStart = asyncStart; } } + // jhipster-needle-application-properties-property-class } diff --git a/src/main/java/org/jhipster/health/config/CRLFLogConverter.java b/src/main/java/org/jhipster/health/config/CRLFLogConverter.java index d445260d..4c12b7bf 100644 --- a/src/main/java/org/jhipster/health/config/CRLFLogConverter.java +++ b/src/main/java/org/jhipster/health/config/CRLFLogConverter.java @@ -4,6 +4,7 @@ import ch.qos.logback.core.pattern.CompositeConverter; import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.slf4j.Marker; import org.slf4j.MarkerFactory; @@ -12,11 +13,22 @@ import org.springframework.boot.ansi.AnsiOutput; import org.springframework.boot.ansi.AnsiStyle; +/** + * Log filter to prevent attackers from forging log entries by submitting input containing CRLF characters. + * CRLF characters are replaced with a red colored _ character. + * + * @see Log Forging Description + * @see JHipster issue + */ public class CRLFLogConverter extends CompositeConverter { public static final Marker CRLF_SAFE_MARKER = MarkerFactory.getMarker("CRLF_SAFE"); - private static final String[] SAFE_LOGGERS = { "org.hibernate" }; + private static final String[] SAFE_LOGGERS = { + "org.hibernate", + "org.springframework.boot.autoconfigure", + "org.springframework.boot.diagnostics", + }; private static final Map ELEMENTS; static { @@ -34,7 +46,8 @@ public class CRLFLogConverter extends CompositeConverter { @Override protected String transform(ILoggingEvent event, String in) { AnsiElement element = ELEMENTS.get(getFirstOption()); - if ((event.getMarker() != null && event.getMarker().contains(CRLF_SAFE_MARKER)) || isLoggerSafe(event)) { + List markers = event.getMarkerList(); + if ((markers != null && !markers.isEmpty() && markers.get(0).contains(CRLF_SAFE_MARKER)) || isLoggerSafe(event)) { return in; } String replacement = element == null ? "_" : toAnsiString("_", element); diff --git a/src/main/java/org/jhipster/health/config/JacksonConfiguration.java b/src/main/java/org/jhipster/health/config/JacksonConfiguration.java index 78059f8e..a7ff7f86 100644 --- a/src/main/java/org/jhipster/health/config/JacksonConfiguration.java +++ b/src/main/java/org/jhipster/health/config/JacksonConfiguration.java @@ -1,12 +1,11 @@ package org.jhipster.health.config; -import com.fasterxml.jackson.datatype.hibernate5.Hibernate5Module; +import com.fasterxml.jackson.datatype.hibernate6.Hibernate6Module; +import com.fasterxml.jackson.datatype.hibernate6.Hibernate6Module.Feature; import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.zalando.problem.jackson.ProblemModule; -import org.zalando.problem.violations.ConstraintViolationProblemModule; @Configuration public class JacksonConfiguration { @@ -29,23 +28,7 @@ public Jdk8Module jdk8TimeModule() { * Support for Hibernate types in Jackson. */ @Bean - public Hibernate5Module hibernate5Module() { - return new Hibernate5Module(); - } - - /* - * Module for serialization/deserialization of RFC7807 Problem. - */ - @Bean - public ProblemModule problemModule() { - return new ProblemModule(); - } - - /* - * Module for serialization/deserialization of ConstraintViolationProblem. - */ - @Bean - public ConstraintViolationProblemModule constraintViolationProblemModule() { - return new ConstraintViolationProblemModule(); + public Hibernate6Module hibernate6Module() { + return new Hibernate6Module().configure(Feature.SERIALIZE_IDENTIFIER_FOR_LAZY_NOT_LOADED_OBJECTS, true); } } diff --git a/src/main/java/org/jhipster/health/config/LiquibaseConfiguration.java b/src/main/java/org/jhipster/health/config/LiquibaseConfiguration.java index 50f3651c..b57c8146 100644 --- a/src/main/java/org/jhipster/health/config/LiquibaseConfiguration.java +++ b/src/main/java/org/jhipster/health/config/LiquibaseConfiguration.java @@ -7,6 +7,7 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.ObjectProvider; import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; import org.springframework.boot.autoconfigure.liquibase.LiquibaseDataSource; import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties; @@ -28,24 +29,35 @@ public LiquibaseConfiguration(Environment env) { this.env = env; } + @Value("${application.liquibase.async-start:true}") + private Boolean asyncStart; + @Bean public SpringLiquibase liquibase( @Qualifier("taskExecutor") Executor executor, - @LiquibaseDataSource ObjectProvider liquibaseDataSource, LiquibaseProperties liquibaseProperties, + @LiquibaseDataSource ObjectProvider liquibaseDataSource, ObjectProvider dataSource, DataSourceProperties dataSourceProperties ) { - // If you don't want Liquibase to start asynchronously, substitute by this: - // SpringLiquibase liquibase = SpringLiquibaseUtil.createSpringLiquibase(liquibaseDataSource.getIfAvailable(), liquibaseProperties, dataSource.getIfUnique(), dataSourceProperties); - SpringLiquibase liquibase = SpringLiquibaseUtil.createAsyncSpringLiquibase( - this.env, - executor, - liquibaseDataSource.getIfAvailable(), - liquibaseProperties, - dataSource.getIfUnique(), - dataSourceProperties - ); + SpringLiquibase liquibase; + if (Boolean.TRUE.equals(asyncStart)) { + liquibase = SpringLiquibaseUtil.createAsyncSpringLiquibase( + this.env, + executor, + liquibaseDataSource.getIfAvailable(), + liquibaseProperties, + dataSource.getIfUnique(), + dataSourceProperties + ); + } else { + liquibase = SpringLiquibaseUtil.createSpringLiquibase( + liquibaseDataSource.getIfAvailable(), + liquibaseProperties, + dataSource.getIfUnique(), + dataSourceProperties + ); + } liquibase.setChangeLog("classpath:config/liquibase/master.xml"); liquibase.setContexts(liquibaseProperties.getContexts()); liquibase.setDefaultSchema(liquibaseProperties.getDefaultSchema()); @@ -54,7 +66,7 @@ public SpringLiquibase liquibase( liquibase.setDatabaseChangeLogLockTable(liquibaseProperties.getDatabaseChangeLogLockTable()); liquibase.setDatabaseChangeLogTable(liquibaseProperties.getDatabaseChangeLogTable()); liquibase.setDropFirst(liquibaseProperties.isDropFirst()); - liquibase.setLabels(liquibaseProperties.getLabels()); + liquibase.setLabelFilter(liquibaseProperties.getLabelFilter()); liquibase.setChangeLogParameters(liquibaseProperties.getParameters()); liquibase.setRollbackFile(liquibaseProperties.getRollbackFile()); liquibase.setTestRollbackOnUpdate(liquibaseProperties.isTestRollbackOnUpdate()); diff --git a/src/main/java/org/jhipster/health/config/LocaleConfiguration.java b/src/main/java/org/jhipster/health/config/LocaleConfiguration.java deleted file mode 100644 index 699848d7..00000000 --- a/src/main/java/org/jhipster/health/config/LocaleConfiguration.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.jhipster.health.config; - -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.servlet.LocaleResolver; -import org.springframework.web.servlet.config.annotation.*; -import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; -import tech.jhipster.config.locale.AngularCookieLocaleResolver; - -@Configuration -public class LocaleConfiguration implements WebMvcConfigurer { - - @Bean - public LocaleResolver localeResolver() { - AngularCookieLocaleResolver cookieLocaleResolver = new AngularCookieLocaleResolver(); - cookieLocaleResolver.setCookieName("NG_TRANSLATE_LANG_KEY"); - return cookieLocaleResolver; - } - - @Override - public void addInterceptors(InterceptorRegistry registry) { - LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor(); - localeChangeInterceptor.setParamName("language"); - registry.addInterceptor(localeChangeInterceptor); - } -} diff --git a/src/main/java/org/jhipster/health/config/SecurityConfiguration.java b/src/main/java/org/jhipster/health/config/SecurityConfiguration.java index 70a605c3..b859b306 100644 --- a/src/main/java/org/jhipster/health/config/SecurityConfiguration.java +++ b/src/main/java/org/jhipster/health/config/SecurityConfiguration.java @@ -1,44 +1,41 @@ package org.jhipster.health.config; +import static org.springframework.security.config.Customizer.withDefaults; +import static org.springframework.security.web.util.matcher.AntPathRequestMatcher.antMatcher; + import org.jhipster.health.security.*; -import org.jhipster.health.security.jwt.*; +import org.jhipster.health.web.filter.SpaWebFilter; import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.env.Environment; +import org.springframework.core.env.Profiles; import org.springframework.http.HttpMethod; -import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer.FrameOptionsConfig; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationEntryPoint; +import org.springframework.security.oauth2.server.resource.web.access.BearerTokenAccessDeniedHandler; import org.springframework.security.web.SecurityFilterChain; -import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.security.web.authentication.www.BasicAuthenticationFilter; import org.springframework.security.web.header.writers.ReferrerPolicyHeaderWriter; -import org.springframework.web.filter.CorsFilter; -import org.zalando.problem.spring.web.advice.security.SecurityProblemSupport; +import org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher; +import org.springframework.web.servlet.handler.HandlerMappingIntrospector; +import tech.jhipster.config.JHipsterConstants; import tech.jhipster.config.JHipsterProperties; -@EnableWebSecurity -@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true) -@Import(SecurityProblemSupport.class) +@Configuration +@EnableMethodSecurity(securedEnabled = true) public class SecurityConfiguration { - private final JHipsterProperties jHipsterProperties; - - private final TokenProvider tokenProvider; + private final Environment env; - private final CorsFilter corsFilter; - private final SecurityProblemSupport problemSupport; + private final JHipsterProperties jHipsterProperties; - public SecurityConfiguration( - TokenProvider tokenProvider, - CorsFilter corsFilter, - JHipsterProperties jHipsterProperties, - SecurityProblemSupport problemSupport - ) { - this.tokenProvider = tokenProvider; - this.corsFilter = corsFilter; - this.problemSupport = problemSupport; + public SecurityConfiguration(Environment env, JHipsterProperties jHipsterProperties) { + this.env = env; this.jHipsterProperties = jHipsterProperties; } @@ -48,58 +45,67 @@ public PasswordEncoder passwordEncoder() { } @Bean - public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { - // @formatter:off + public SecurityFilterChain filterChain(HttpSecurity http, MvcRequestMatcher.Builder mvc) throws Exception { http - .csrf() - .ignoringAntMatchers("/h2-console/**") - .disable() - .addFilterBefore(corsFilter, UsernamePasswordAuthenticationFilter.class) - .exceptionHandling() - .authenticationEntryPoint(problemSupport) - .accessDeniedHandler(problemSupport) - .and() - .headers() - .contentSecurityPolicy(jHipsterProperties.getSecurity().getContentSecurityPolicy()) - .and() - .referrerPolicy(ReferrerPolicyHeaderWriter.ReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN) - .and() - .permissionsPolicy().policy("camera=(), fullscreen=(self), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), midi=(), payment=(), sync-xhr=()") - .and() - .frameOptions().sameOrigin() - .and() - .sessionManagement() - .sessionCreationPolicy(SessionCreationPolicy.STATELESS) - .and() - .authorizeRequests() - .antMatchers(HttpMethod.OPTIONS, "/**").permitAll() - .antMatchers("/app/**/*.{js,html}").permitAll() - .antMatchers("/i18n/**").permitAll() - .antMatchers("/content/**").permitAll() - .antMatchers("/swagger-ui/**").permitAll() - .antMatchers("/test/**").permitAll() - .antMatchers("/h2-console/**").permitAll() - .antMatchers("/api/authenticate").permitAll() - .antMatchers("/api/register").permitAll() - .antMatchers("/api/activate").permitAll() - .antMatchers("/api/account/reset-password/init").permitAll() - .antMatchers("/api/account/reset-password/finish").permitAll() - .antMatchers("/api/admin/**").hasAuthority(AuthoritiesConstants.ADMIN) - .antMatchers("/api/**").authenticated() - .antMatchers("/management/health").permitAll() - .antMatchers("/management/health/**").permitAll() - .antMatchers("/management/info").permitAll() - .antMatchers("/management/prometheus").permitAll() - .antMatchers("/management/**").hasAuthority(AuthoritiesConstants.ADMIN) - .and() - .httpBasic() - .and() - .apply(securityConfigurerAdapter()); + .cors(withDefaults()) + .csrf(csrf -> csrf.disable()) + .addFilterAfter(new SpaWebFilter(), BasicAuthenticationFilter.class) + .headers( + headers -> + headers + .contentSecurityPolicy(csp -> csp.policyDirectives(jHipsterProperties.getSecurity().getContentSecurityPolicy())) + .frameOptions(FrameOptionsConfig::sameOrigin) + .referrerPolicy( + referrer -> referrer.policy(ReferrerPolicyHeaderWriter.ReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN) + ) + .permissionsPolicy( + permissions -> + permissions.policy( + "camera=(), fullscreen=(self), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), midi=(), payment=(), sync-xhr=()" + ) + ) + ) + .authorizeHttpRequests( + authz -> + // prettier-ignore + authz + .requestMatchers(mvc.pattern("/index.html"), mvc.pattern("/*.js"), mvc.pattern("/*.txt"), mvc.pattern("/*.json"), mvc.pattern("/*.map"), mvc.pattern("/*.css")).permitAll() + .requestMatchers(mvc.pattern("/*.ico"), mvc.pattern("/*.png"), mvc.pattern("/*.svg"), mvc.pattern("/*.webapp")).permitAll() + .requestMatchers(mvc.pattern("/app/**")).permitAll() + .requestMatchers(mvc.pattern("/i18n/**")).permitAll() + .requestMatchers(mvc.pattern("/content/**")).permitAll() + .requestMatchers(mvc.pattern("/swagger-ui/**")).permitAll() + .requestMatchers(mvc.pattern(HttpMethod.POST, "/api/authenticate")).permitAll() + .requestMatchers(mvc.pattern(HttpMethod.GET, "/api/authenticate")).permitAll() + .requestMatchers(mvc.pattern("/api/register")).permitAll() + .requestMatchers(mvc.pattern("/api/activate")).permitAll() + .requestMatchers(mvc.pattern("/api/account/reset-password/init")).permitAll() + .requestMatchers(mvc.pattern("/api/account/reset-password/finish")).permitAll() + .requestMatchers(mvc.pattern("/api/admin/**")).hasAuthority(AuthoritiesConstants.ADMIN) + .requestMatchers(mvc.pattern("/api/**")).authenticated() + .requestMatchers(mvc.pattern("/v3/api-docs/**")).hasAuthority(AuthoritiesConstants.ADMIN) + .requestMatchers(mvc.pattern("/management/health")).permitAll() + .requestMatchers(mvc.pattern("/management/health/**")).permitAll() + .requestMatchers(mvc.pattern("/management/info")).permitAll() + .requestMatchers(mvc.pattern("/management/prometheus")).permitAll() + .requestMatchers(mvc.pattern("/management/**")).hasAuthority(AuthoritiesConstants.ADMIN) + ) + .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) + .exceptionHandling( + exceptions -> + exceptions + .authenticationEntryPoint(new BearerTokenAuthenticationEntryPoint()) + .accessDeniedHandler(new BearerTokenAccessDeniedHandler()) + ) + .oauth2ResourceServer(oauth2 -> oauth2.jwt(withDefaults())); + if (env.acceptsProfiles(Profiles.of(JHipsterConstants.SPRING_PROFILE_DEVELOPMENT))) { + http.authorizeHttpRequests(authz -> authz.requestMatchers(antMatcher("/h2-console/**")).permitAll()); + } return http.build(); - // @formatter:on } - private JWTConfigurer securityConfigurerAdapter() { - return new JWTConfigurer(tokenProvider); + @Bean + MvcRequestMatcher.Builder mvc(HandlerMappingIntrospector introspector) { + return new MvcRequestMatcher.Builder(introspector); } } diff --git a/src/main/java/org/jhipster/health/config/SecurityJwtConfiguration.java b/src/main/java/org/jhipster/health/config/SecurityJwtConfiguration.java new file mode 100644 index 00000000..8e964b58 --- /dev/null +++ b/src/main/java/org/jhipster/health/config/SecurityJwtConfiguration.java @@ -0,0 +1,76 @@ +package org.jhipster.health.config; + +import static org.jhipster.health.security.SecurityUtils.AUTHORITIES_KEY; +import static org.jhipster.health.security.SecurityUtils.JWT_ALGORITHM; + +import com.nimbusds.jose.jwk.source.ImmutableSecret; +import com.nimbusds.jose.util.Base64; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; +import org.jhipster.health.management.SecurityMetersService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.oauth2.jwt.JwtDecoder; +import org.springframework.security.oauth2.jwt.JwtEncoder; +import org.springframework.security.oauth2.jwt.NimbusJwtDecoder; +import org.springframework.security.oauth2.jwt.NimbusJwtEncoder; +import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter; +import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter; + +@Configuration +public class SecurityJwtConfiguration { + + private final Logger log = LoggerFactory.getLogger(SecurityJwtConfiguration.class); + + @Value("${jhipster.security.authentication.jwt.base64-secret}") + private String jwtKey; + + @Bean + public JwtDecoder jwtDecoder(SecurityMetersService metersService) { + NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withSecretKey(getSecretKey()).macAlgorithm(JWT_ALGORITHM).build(); + return token -> { + try { + return jwtDecoder.decode(token); + } catch (Exception e) { + if (e.getMessage().contains("Invalid signature")) { + metersService.trackTokenInvalidSignature(); + } else if (e.getMessage().contains("Jwt expired at")) { + metersService.trackTokenExpired(); + } else if ( + e.getMessage().contains("Invalid JWT serialization") || + e.getMessage().contains("Malformed token") || + e.getMessage().contains("Invalid unsecured/JWS/JWE") + ) { + metersService.trackTokenMalformed(); + } else { + log.error("Unknown JWT error {}", e.getMessage()); + } + throw e; + } + }; + } + + @Bean + public JwtEncoder jwtEncoder() { + return new NimbusJwtEncoder(new ImmutableSecret<>(getSecretKey())); + } + + @Bean + public JwtAuthenticationConverter jwtAuthenticationConverter() { + JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter(); + grantedAuthoritiesConverter.setAuthorityPrefix(""); + grantedAuthoritiesConverter.setAuthoritiesClaimName(AUTHORITIES_KEY); + + JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter(); + jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter); + return jwtAuthenticationConverter; + } + + private SecretKey getSecretKey() { + byte[] keyBytes = Base64.from(jwtKey).decode(); + return new SecretKeySpec(keyBytes, 0, keyBytes.length, JWT_ALGORITHM.getName()); + } +} diff --git a/src/main/java/org/jhipster/health/config/StaticResourcesWebConfiguration.java b/src/main/java/org/jhipster/health/config/StaticResourcesWebConfiguration.java index a16c1470..6a8f5c01 100644 --- a/src/main/java/org/jhipster/health/config/StaticResourcesWebConfiguration.java +++ b/src/main/java/org/jhipster/health/config/StaticResourcesWebConfiguration.java @@ -14,20 +14,8 @@ @Profile({ JHipsterConstants.SPRING_PROFILE_PRODUCTION }) public class StaticResourcesWebConfiguration implements WebMvcConfigurer { - protected static final String[] RESOURCE_LOCATIONS = new String[] { - "classpath:/static/", - "classpath:/static/content/", - "classpath:/static/i18n/", - }; - protected static final String[] RESOURCE_PATHS = new String[] { - "/*.js", - "/*.css", - "/*.svg", - "/*.png", - "*.ico", - "/content/**", - "/i18n/*", - }; + protected static final String[] RESOURCE_LOCATIONS = { "classpath:/static/", "classpath:/static/content/", "classpath:/static/i18n/" }; + protected static final String[] RESOURCE_PATHS = { "/*.js", "/*.css", "/*.svg", "/*.png", "*.ico", "/content/**", "/i18n/*" }; private final JHipsterProperties jhipsterProperties; diff --git a/src/main/java/org/jhipster/health/config/WebConfigurer.java b/src/main/java/org/jhipster/health/config/WebConfigurer.java index 8bc71e0b..bcedf8e5 100644 --- a/src/main/java/org/jhipster/health/config/WebConfigurer.java +++ b/src/main/java/org/jhipster/health/config/WebConfigurer.java @@ -2,10 +2,10 @@ import static java.net.URLDecoder.decode; +import jakarta.servlet.*; import java.io.File; import java.nio.charset.StandardCharsets; import java.nio.file.Paths; -import javax.servlet.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.web.server.*; @@ -41,7 +41,7 @@ public WebConfigurer(Environment env, JHipsterProperties jHipsterProperties) { } @Override - public void onStartup(ServletContext servletContext) throws ServletException { + public void onStartup(ServletContext servletContext) { if (env.getActiveProfiles().length != 0) { log.info("Web application configuration, using profiles: {}", (Object[]) env.getActiveProfiles()); } @@ -62,8 +62,7 @@ public void customize(WebServerFactory server) { } private void setLocationForStaticAssets(WebServerFactory server) { - if (server instanceof ConfigurableServletWebServerFactory) { - ConfigurableServletWebServerFactory servletWebServer = (ConfigurableServletWebServerFactory) server; + if (server instanceof ConfigurableServletWebServerFactory servletWebServer) { File root; String prefixPath = resolvePathPrefix(); root = new File(prefixPath + "build/resources/main/static/"); diff --git a/src/main/java/org/jhipster/health/config/package-info.java b/src/main/java/org/jhipster/health/config/package-info.java index 05a2884f..f3c08a03 100644 --- a/src/main/java/org/jhipster/health/config/package-info.java +++ b/src/main/java/org/jhipster/health/config/package-info.java @@ -1,4 +1,4 @@ /** - * Spring Framework configuration files. + * Application configuration. */ package org.jhipster.health.config; diff --git a/src/main/java/org/jhipster/health/domain/AbstractAuditingEntity.java b/src/main/java/org/jhipster/health/domain/AbstractAuditingEntity.java index 7e1004af..b0ffae89 100644 --- a/src/main/java/org/jhipster/health/domain/AbstractAuditingEntity.java +++ b/src/main/java/org/jhipster/health/domain/AbstractAuditingEntity.java @@ -1,11 +1,11 @@ package org.jhipster.health.domain; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import jakarta.persistence.Column; +import jakarta.persistence.EntityListeners; +import jakarta.persistence.MappedSuperclass; import java.io.Serializable; import java.time.Instant; -import javax.persistence.Column; -import javax.persistence.EntityListeners; -import javax.persistence.MappedSuperclass; import org.springframework.data.annotation.CreatedBy; import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedBy; diff --git a/src/main/java/org/jhipster/health/domain/Authority.java b/src/main/java/org/jhipster/health/domain/Authority.java index deae5ff9..51544116 100644 --- a/src/main/java/org/jhipster/health/domain/Authority.java +++ b/src/main/java/org/jhipster/health/domain/Authority.java @@ -1,40 +1,74 @@ package org.jhipster.health.domain; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import jakarta.persistence.*; +import jakarta.validation.constraints.*; import java.io.Serializable; import java.util.Objects; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.Table; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Size; import org.hibernate.annotations.Cache; import org.hibernate.annotations.CacheConcurrencyStrategy; +import org.springframework.data.domain.Persistable; /** - * An authority (a security role) used by Spring Security. + * A Authority. */ @Entity @Table(name = "jhi_authority") -@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) -public class Authority implements Serializable { +@Cache(usage = CacheConcurrencyStrategy.READ_WRITE) +@JsonIgnoreProperties(value = { "new", "id" }) +@SuppressWarnings("common-java:DuplicatedBlocks") +public class Authority implements Serializable, Persistable { private static final long serialVersionUID = 1L; @NotNull @Size(max = 50) @Id - @Column(length = 50) + @Column(name = "name", length = 50, nullable = false) private String name; + @Transient + private boolean isPersisted; + + // jhipster-needle-entity-add-field - JHipster will add fields here + public String getName() { - return name; + return this.name; + } + + public Authority name(String name) { + this.setName(name); + return this; } public void setName(String name) { this.name = name; } + @PostLoad + @PostPersist + public void updateEntityState() { + this.setIsPersisted(); + } + + @Override + public String getId() { + return this.name; + } + + @Transient + @Override + public boolean isNew() { + return !this.isPersisted; + } + + public Authority setIsPersisted() { + this.isPersisted = true; + return this; + } + + // jhipster-needle-entity-add-getters-setters - JHipster will add getters and setters here + @Override public boolean equals(Object o) { if (this == o) { @@ -43,19 +77,19 @@ public boolean equals(Object o) { if (!(o instanceof Authority)) { return false; } - return Objects.equals(name, ((Authority) o).name); + return getName() != null && getName().equals(((Authority) o).getName()); } @Override public int hashCode() { - return Objects.hashCode(name); + return Objects.hashCode(getName()); } // prettier-ignore @Override public String toString() { return "Authority{" + - "name='" + name + '\'' + + "name=" + getName() + "}"; } } diff --git a/src/main/java/org/jhipster/health/domain/BloodPressure.java b/src/main/java/org/jhipster/health/domain/BloodPressure.java index 5ebf7230..dc0fddda 100644 --- a/src/main/java/org/jhipster/health/domain/BloodPressure.java +++ b/src/main/java/org/jhipster/health/domain/BloodPressure.java @@ -1,9 +1,9 @@ package org.jhipster.health.domain; +import jakarta.persistence.*; +import jakarta.validation.constraints.*; import java.io.Serializable; import java.time.ZonedDateTime; -import javax.persistence.*; -import javax.validation.constraints.*; import org.hibernate.annotations.Cache; import org.hibernate.annotations.CacheConcurrencyStrategy; @@ -31,13 +31,15 @@ public class BloodPressure implements Serializable { @NotNull @Column(name = "systolic", nullable = false) + @org.springframework.data.elasticsearch.annotations.Field(type = org.springframework.data.elasticsearch.annotations.FieldType.Integer) private Integer systolic; @NotNull @Column(name = "diastolic", nullable = false) + @org.springframework.data.elasticsearch.annotations.Field(type = org.springframework.data.elasticsearch.annotations.FieldType.Integer) private Integer diastolic; - @ManyToOne + @ManyToOne(fetch = FetchType.LAZY) private User user; // jhipster-needle-entity-add-field - JHipster will add fields here @@ -117,7 +119,7 @@ public boolean equals(Object o) { if (!(o instanceof BloodPressure)) { return false; } - return id != null && id.equals(((BloodPressure) o).id); + return getId() != null && getId().equals(((BloodPressure) o).getId()); } @Override diff --git a/src/main/java/org/jhipster/health/domain/Points.java b/src/main/java/org/jhipster/health/domain/Points.java index ec629b52..ab0a2cac 100644 --- a/src/main/java/org/jhipster/health/domain/Points.java +++ b/src/main/java/org/jhipster/health/domain/Points.java @@ -1,9 +1,9 @@ package org.jhipster.health.domain; +import jakarta.persistence.*; +import jakarta.validation.constraints.*; import java.io.Serializable; import java.time.LocalDate; -import javax.persistence.*; -import javax.validation.constraints.*; import org.hibernate.annotations.Cache; import org.hibernate.annotations.CacheConcurrencyStrategy; @@ -30,19 +30,23 @@ public class Points implements Serializable { private LocalDate date; @Column(name = "exercise") + @org.springframework.data.elasticsearch.annotations.Field(type = org.springframework.data.elasticsearch.annotations.FieldType.Integer) private Integer exercise; @Column(name = "meals") + @org.springframework.data.elasticsearch.annotations.Field(type = org.springframework.data.elasticsearch.annotations.FieldType.Integer) private Integer meals; @Column(name = "alcohol") + @org.springframework.data.elasticsearch.annotations.Field(type = org.springframework.data.elasticsearch.annotations.FieldType.Integer) private Integer alcohol; @Size(max = 140) @Column(name = "notes", length = 140) + @org.springframework.data.elasticsearch.annotations.Field(type = org.springframework.data.elasticsearch.annotations.FieldType.Text) private String notes; - @ManyToOne + @ManyToOne(fetch = FetchType.LAZY) private User user; // jhipster-needle-entity-add-field - JHipster will add fields here @@ -148,7 +152,7 @@ public boolean equals(Object o) { if (!(o instanceof Points)) { return false; } - return id != null && id.equals(((Points) o).id); + return getId() != null && getId().equals(((Points) o).getId()); } @Override diff --git a/src/main/java/org/jhipster/health/domain/Preferences.java b/src/main/java/org/jhipster/health/domain/Preferences.java index f6c49f1b..9679da38 100644 --- a/src/main/java/org/jhipster/health/domain/Preferences.java +++ b/src/main/java/org/jhipster/health/domain/Preferences.java @@ -1,8 +1,8 @@ package org.jhipster.health.domain; +import jakarta.persistence.*; +import jakarta.validation.constraints.*; import java.io.Serializable; -import javax.persistence.*; -import javax.validation.constraints.*; import org.hibernate.annotations.Cache; import org.hibernate.annotations.CacheConcurrencyStrategy; import org.jhipster.health.domain.enumeration.Units; @@ -29,14 +29,16 @@ public class Preferences implements Serializable { @Min(value = 10) @Max(value = 21) @Column(name = "weekly_goal", nullable = false) + @org.springframework.data.elasticsearch.annotations.Field(type = org.springframework.data.elasticsearch.annotations.FieldType.Integer) private Integer weeklyGoal; @NotNull @Enumerated(EnumType.STRING) @Column(name = "weight_units", nullable = false) + @org.springframework.data.elasticsearch.annotations.Field(type = org.springframework.data.elasticsearch.annotations.FieldType.Keyword) private Units weightUnits; - @OneToOne + @OneToOne(fetch = FetchType.LAZY) @JoinColumn(unique = true) private User user; @@ -104,7 +106,7 @@ public boolean equals(Object o) { if (!(o instanceof Preferences)) { return false; } - return id != null && id.equals(((Preferences) o).id); + return getId() != null && getId().equals(((Preferences) o).getId()); } @Override diff --git a/src/main/java/org/jhipster/health/domain/User.java b/src/main/java/org/jhipster/health/domain/User.java index 792010ea..fef946f5 100644 --- a/src/main/java/org/jhipster/health/domain/User.java +++ b/src/main/java/org/jhipster/health/domain/User.java @@ -1,16 +1,16 @@ package org.jhipster.health.domain; import com.fasterxml.jackson.annotation.JsonIgnore; +import jakarta.persistence.*; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; import java.io.Serializable; import java.time.Instant; import java.util.HashSet; import java.util.Locale; import java.util.Set; -import javax.persistence.*; -import javax.validation.constraints.Email; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Pattern; -import javax.validation.constraints.Size; import org.apache.commons.lang3.StringUtils; import org.hibernate.annotations.BatchSize; import org.hibernate.annotations.Cache; diff --git a/src/main/java/org/jhipster/health/domain/Weight.java b/src/main/java/org/jhipster/health/domain/Weight.java index 96f4f8bb..3c867eb1 100644 --- a/src/main/java/org/jhipster/health/domain/Weight.java +++ b/src/main/java/org/jhipster/health/domain/Weight.java @@ -1,9 +1,9 @@ package org.jhipster.health.domain; +import jakarta.persistence.*; +import jakarta.validation.constraints.*; import java.io.Serializable; import java.time.ZonedDateTime; -import javax.persistence.*; -import javax.validation.constraints.*; import org.hibernate.annotations.Cache; import org.hibernate.annotations.CacheConcurrencyStrategy; @@ -33,7 +33,7 @@ public class Weight implements Serializable { @Column(name = "weight", nullable = false) private Double weight; - @ManyToOne + @ManyToOne(fetch = FetchType.LAZY) private User user; // jhipster-needle-entity-add-field - JHipster will add fields here @@ -100,7 +100,7 @@ public boolean equals(Object o) { if (!(o instanceof Weight)) { return false; } - return id != null && id.equals(((Weight) o).id); + return getId() != null && getId().equals(((Weight) o).getId()); } @Override diff --git a/src/main/java/org/jhipster/health/domain/enumeration/package-info.java b/src/main/java/org/jhipster/health/domain/enumeration/package-info.java new file mode 100644 index 00000000..4f179b5b --- /dev/null +++ b/src/main/java/org/jhipster/health/domain/enumeration/package-info.java @@ -0,0 +1,4 @@ +/** + * This package file was generated by JHipster + */ +package org.jhipster.health.domain.enumeration; diff --git a/src/main/java/org/jhipster/health/domain/package-info.java b/src/main/java/org/jhipster/health/domain/package-info.java index ed27d834..1c48c888 100644 --- a/src/main/java/org/jhipster/health/domain/package-info.java +++ b/src/main/java/org/jhipster/health/domain/package-info.java @@ -1,4 +1,4 @@ /** - * JPA domain objects. + * Domain objects. */ package org.jhipster.health.domain; diff --git a/src/main/java/org/jhipster/health/management/package-info.java b/src/main/java/org/jhipster/health/management/package-info.java new file mode 100644 index 00000000..794d9f5b --- /dev/null +++ b/src/main/java/org/jhipster/health/management/package-info.java @@ -0,0 +1,4 @@ +/** + * Application management. + */ +package org.jhipster.health.management; diff --git a/src/main/java/org/jhipster/health/package-info.java b/src/main/java/org/jhipster/health/package-info.java new file mode 100644 index 00000000..c17271cd --- /dev/null +++ b/src/main/java/org/jhipster/health/package-info.java @@ -0,0 +1,4 @@ +/** + * Application root. + */ +package org.jhipster.health; diff --git a/src/main/java/org/jhipster/health/repository/AuthorityRepository.java b/src/main/java/org/jhipster/health/repository/AuthorityRepository.java index 494d5827..83d8333d 100644 --- a/src/main/java/org/jhipster/health/repository/AuthorityRepository.java +++ b/src/main/java/org/jhipster/health/repository/AuthorityRepository.java @@ -1,9 +1,12 @@ package org.jhipster.health.repository; import org.jhipster.health.domain.Authority; -import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.*; +import org.springframework.stereotype.Repository; /** - * Spring Data JPA repository for the {@link Authority} entity. + * Spring Data JPA repository for the Authority entity. */ +@SuppressWarnings("unused") +@Repository public interface AuthorityRepository extends JpaRepository {} diff --git a/src/main/java/org/jhipster/health/repository/BloodPressureRepository.java b/src/main/java/org/jhipster/health/repository/BloodPressureRepository.java index c8d7527e..5a9f4f4f 100644 --- a/src/main/java/org/jhipster/health/repository/BloodPressureRepository.java +++ b/src/main/java/org/jhipster/health/repository/BloodPressureRepository.java @@ -14,7 +14,7 @@ */ @Repository public interface BloodPressureRepository extends JpaRepository { - @Query("select bloodPressure from BloodPressure bloodPressure where bloodPressure.user.login = ?#{principal.username}") + @Query("select bloodPressure from BloodPressure bloodPressure where bloodPressure.user.login = ?#{authentication.name}") List findByUserIsCurrentUser(); default Optional findOneWithEagerRelationships(Long id) { @@ -30,12 +30,12 @@ default Page findAllWithEagerRelationships(Pageable pageable) { } @Query( - value = "select distinct bloodPressure from BloodPressure bloodPressure left join fetch bloodPressure.user", - countQuery = "select count(distinct bloodPressure) from BloodPressure bloodPressure" + value = "select bloodPressure from BloodPressure bloodPressure left join fetch bloodPressure.user", + countQuery = "select count(bloodPressure) from BloodPressure bloodPressure" ) Page findAllWithToOneRelationships(Pageable pageable); - @Query("select distinct bloodPressure from BloodPressure bloodPressure left join fetch bloodPressure.user") + @Query("select bloodPressure from BloodPressure bloodPressure left join fetch bloodPressure.user") List findAllWithToOneRelationships(); @Query("select bloodPressure from BloodPressure bloodPressure left join fetch bloodPressure.user where bloodPressure.id =:id") diff --git a/src/main/java/org/jhipster/health/repository/PointsRepository.java b/src/main/java/org/jhipster/health/repository/PointsRepository.java index 5c1e9ce1..66e05a33 100644 --- a/src/main/java/org/jhipster/health/repository/PointsRepository.java +++ b/src/main/java/org/jhipster/health/repository/PointsRepository.java @@ -14,7 +14,7 @@ */ @Repository public interface PointsRepository extends JpaRepository { - @Query("select points from Points points where points.user.login = ?#{principal.username}") + @Query("select points from Points points where points.user.login = ?#{authentication.name}") List findByUserIsCurrentUser(); default Optional findOneWithEagerRelationships(Long id) { @@ -29,13 +29,10 @@ default Page findAllWithEagerRelationships(Pageable pageable) { return this.findAllWithToOneRelationships(pageable); } - @Query( - value = "select distinct points from Points points left join fetch points.user", - countQuery = "select count(distinct points) from Points points" - ) + @Query(value = "select points from Points points left join fetch points.user", countQuery = "select count(points) from Points points") Page findAllWithToOneRelationships(Pageable pageable); - @Query("select distinct points from Points points left join fetch points.user") + @Query("select points from Points points left join fetch points.user") List findAllWithToOneRelationships(); @Query("select points from Points points left join fetch points.user where points.id =:id") diff --git a/src/main/java/org/jhipster/health/repository/PreferencesRepository.java b/src/main/java/org/jhipster/health/repository/PreferencesRepository.java index d599c839..334ece2c 100644 --- a/src/main/java/org/jhipster/health/repository/PreferencesRepository.java +++ b/src/main/java/org/jhipster/health/repository/PreferencesRepository.java @@ -27,12 +27,12 @@ default Page findAllWithEagerRelationships(Pageable pageable) { } @Query( - value = "select distinct preferences from Preferences preferences left join fetch preferences.user", - countQuery = "select count(distinct preferences) from Preferences preferences" + value = "select preferences from Preferences preferences left join fetch preferences.user", + countQuery = "select count(preferences) from Preferences preferences" ) Page findAllWithToOneRelationships(Pageable pageable); - @Query("select distinct preferences from Preferences preferences left join fetch preferences.user") + @Query("select preferences from Preferences preferences left join fetch preferences.user") List findAllWithToOneRelationships(); @Query("select preferences from Preferences preferences left join fetch preferences.user where preferences.id =:id") diff --git a/src/main/java/org/jhipster/health/repository/WeightRepository.java b/src/main/java/org/jhipster/health/repository/WeightRepository.java index 0eb0d1b7..2b0fedd4 100644 --- a/src/main/java/org/jhipster/health/repository/WeightRepository.java +++ b/src/main/java/org/jhipster/health/repository/WeightRepository.java @@ -14,7 +14,7 @@ */ @Repository public interface WeightRepository extends JpaRepository { - @Query("select weight from Weight weight where weight.user.login = ?#{principal.username}") + @Query("select weight from Weight weight where weight.user.login = ?#{authentication.name}") List findByUserIsCurrentUser(); default Optional findOneWithEagerRelationships(Long id) { @@ -29,13 +29,10 @@ default Page findAllWithEagerRelationships(Pageable pageable) { return this.findAllWithToOneRelationships(pageable); } - @Query( - value = "select distinct weight from Weight weight left join fetch weight.user", - countQuery = "select count(distinct weight) from Weight weight" - ) + @Query(value = "select weight from Weight weight left join fetch weight.user", countQuery = "select count(weight) from Weight weight") Page findAllWithToOneRelationships(Pageable pageable); - @Query("select distinct weight from Weight weight left join fetch weight.user") + @Query("select weight from Weight weight left join fetch weight.user") List findAllWithToOneRelationships(); @Query("select weight from Weight weight left join fetch weight.user where weight.id =:id") diff --git a/src/main/java/org/jhipster/health/repository/package-info.java b/src/main/java/org/jhipster/health/repository/package-info.java index 30270b82..f48781ef 100644 --- a/src/main/java/org/jhipster/health/repository/package-info.java +++ b/src/main/java/org/jhipster/health/repository/package-info.java @@ -1,4 +1,4 @@ /** - * Spring Data JPA repositories. + * Repository layer. */ package org.jhipster.health.repository; diff --git a/src/main/java/org/jhipster/health/repository/search/BloodPressureSearchRepository.java b/src/main/java/org/jhipster/health/repository/search/BloodPressureSearchRepository.java index 4e82fb3f..1ed6573f 100644 --- a/src/main/java/org/jhipster/health/repository/search/BloodPressureSearchRepository.java +++ b/src/main/java/org/jhipster/health/repository/search/BloodPressureSearchRepository.java @@ -1,21 +1,19 @@ package org.jhipster.health.repository.search; -import static org.elasticsearch.index.query.QueryBuilders.queryStringQuery; - -import java.util.List; +import co.elastic.clients.elasticsearch._types.query_dsl.QueryStringQuery; import java.util.List; -import java.util.stream.Collectors; import org.jhipster.health.domain.BloodPressure; import org.jhipster.health.repository.BloodPressureRepository; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; -import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate; +import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate; +import org.springframework.data.elasticsearch.client.elc.NativeQuery; import org.springframework.data.elasticsearch.core.SearchHit; import org.springframework.data.elasticsearch.core.SearchHits; -import org.springframework.data.elasticsearch.core.query.NativeSearchQuery; import org.springframework.data.elasticsearch.core.query.Query; import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; +import org.springframework.scheduling.annotation.Async; /** * Spring Data Elasticsearch repository for the {@link BloodPressure} entity. @@ -28,29 +26,33 @@ interface BloodPressureSearchRepositoryInternal { Page search(Query query); + @Async void index(BloodPressure entity); + + @Async + void deleteFromIndexById(Long id); } class BloodPressureSearchRepositoryInternalImpl implements BloodPressureSearchRepositoryInternal { - private final ElasticsearchRestTemplate elasticsearchTemplate; + private final ElasticsearchTemplate elasticsearchTemplate; private final BloodPressureRepository repository; - BloodPressureSearchRepositoryInternalImpl(ElasticsearchRestTemplate elasticsearchTemplate, BloodPressureRepository repository) { + BloodPressureSearchRepositoryInternalImpl(ElasticsearchTemplate elasticsearchTemplate, BloodPressureRepository repository) { this.elasticsearchTemplate = elasticsearchTemplate; this.repository = repository; } @Override public Page search(String query, Pageable pageable) { - NativeSearchQuery nativeSearchQuery = new NativeSearchQuery(queryStringQuery(query)); - return search(nativeSearchQuery.setPageable(pageable)); + NativeQuery nativeQuery = new NativeQuery(QueryStringQuery.of(qs -> qs.query(query))._toQuery()); + return search(nativeQuery.setPageable(pageable)); } @Override public Page search(Query query) { SearchHits searchHits = elasticsearchTemplate.search(query, BloodPressure.class); - List hits = searchHits.map(SearchHit::getContent).stream().collect(Collectors.toList()); + List hits = searchHits.map(SearchHit::getContent).stream().toList(); return new PageImpl<>(hits, query.getPageable(), searchHits.getTotalHits()); } @@ -58,4 +60,9 @@ public Page search(Query query) { public void index(BloodPressure entity) { repository.findOneWithEagerRelationships(entity.getId()).ifPresent(elasticsearchTemplate::save); } + + @Override + public void deleteFromIndexById(Long id) { + elasticsearchTemplate.delete(String.valueOf(id), BloodPressure.class); + } } diff --git a/src/main/java/org/jhipster/health/repository/search/PointsSearchRepository.java b/src/main/java/org/jhipster/health/repository/search/PointsSearchRepository.java index c31095d6..ee99ff72 100644 --- a/src/main/java/org/jhipster/health/repository/search/PointsSearchRepository.java +++ b/src/main/java/org/jhipster/health/repository/search/PointsSearchRepository.java @@ -1,21 +1,19 @@ package org.jhipster.health.repository.search; -import static org.elasticsearch.index.query.QueryBuilders.queryStringQuery; - -import java.util.List; +import co.elastic.clients.elasticsearch._types.query_dsl.QueryStringQuery; import java.util.List; -import java.util.stream.Collectors; import org.jhipster.health.domain.Points; import org.jhipster.health.repository.PointsRepository; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; -import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate; +import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate; +import org.springframework.data.elasticsearch.client.elc.NativeQuery; import org.springframework.data.elasticsearch.core.SearchHit; import org.springframework.data.elasticsearch.core.SearchHits; -import org.springframework.data.elasticsearch.core.query.NativeSearchQuery; import org.springframework.data.elasticsearch.core.query.Query; import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; +import org.springframework.scheduling.annotation.Async; /** * Spring Data Elasticsearch repository for the {@link Points} entity. @@ -27,29 +25,33 @@ interface PointsSearchRepositoryInternal { Page search(Query query); + @Async void index(Points entity); + + @Async + void deleteFromIndexById(Long id); } class PointsSearchRepositoryInternalImpl implements PointsSearchRepositoryInternal { - private final ElasticsearchRestTemplate elasticsearchTemplate; + private final ElasticsearchTemplate elasticsearchTemplate; private final PointsRepository repository; - PointsSearchRepositoryInternalImpl(ElasticsearchRestTemplate elasticsearchTemplate, PointsRepository repository) { + PointsSearchRepositoryInternalImpl(ElasticsearchTemplate elasticsearchTemplate, PointsRepository repository) { this.elasticsearchTemplate = elasticsearchTemplate; this.repository = repository; } @Override public Page search(String query, Pageable pageable) { - NativeSearchQuery nativeSearchQuery = new NativeSearchQuery(queryStringQuery(query)); - return search(nativeSearchQuery.setPageable(pageable)); + NativeQuery nativeQuery = new NativeQuery(QueryStringQuery.of(qs -> qs.query(query))._toQuery()); + return search(nativeQuery.setPageable(pageable)); } @Override public Page search(Query query) { SearchHits searchHits = elasticsearchTemplate.search(query, Points.class); - List hits = searchHits.map(SearchHit::getContent).stream().collect(Collectors.toList()); + List hits = searchHits.map(SearchHit::getContent).stream().toList(); return new PageImpl<>(hits, query.getPageable(), searchHits.getTotalHits()); } @@ -57,4 +59,9 @@ public Page search(Query query) { public void index(Points entity) { repository.findOneWithEagerRelationships(entity.getId()).ifPresent(elasticsearchTemplate::save); } + + @Override + public void deleteFromIndexById(Long id) { + elasticsearchTemplate.delete(String.valueOf(id), Points.class); + } } diff --git a/src/main/java/org/jhipster/health/repository/search/PreferencesSearchRepository.java b/src/main/java/org/jhipster/health/repository/search/PreferencesSearchRepository.java index ede49fde..f1209101 100644 --- a/src/main/java/org/jhipster/health/repository/search/PreferencesSearchRepository.java +++ b/src/main/java/org/jhipster/health/repository/search/PreferencesSearchRepository.java @@ -1,15 +1,15 @@ package org.jhipster.health.repository.search; -import static org.elasticsearch.index.query.QueryBuilders.queryStringQuery; - +import co.elastic.clients.elasticsearch._types.query_dsl.QueryStringQuery; import java.util.stream.Stream; import org.jhipster.health.domain.Preferences; import org.jhipster.health.repository.PreferencesRepository; -import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate; +import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate; +import org.springframework.data.elasticsearch.client.elc.NativeQuery; import org.springframework.data.elasticsearch.core.SearchHit; -import org.springframework.data.elasticsearch.core.query.NativeSearchQuery; import org.springframework.data.elasticsearch.core.query.Query; import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; +import org.springframework.scheduling.annotation.Async; /** * Spring Data Elasticsearch repository for the {@link Preferences} entity. @@ -21,23 +21,27 @@ interface PreferencesSearchRepositoryInternal { Stream search(Query query); + @Async void index(Preferences entity); + + @Async + void deleteFromIndexById(Long id); } class PreferencesSearchRepositoryInternalImpl implements PreferencesSearchRepositoryInternal { - private final ElasticsearchRestTemplate elasticsearchTemplate; + private final ElasticsearchTemplate elasticsearchTemplate; private final PreferencesRepository repository; - PreferencesSearchRepositoryInternalImpl(ElasticsearchRestTemplate elasticsearchTemplate, PreferencesRepository repository) { + PreferencesSearchRepositoryInternalImpl(ElasticsearchTemplate elasticsearchTemplate, PreferencesRepository repository) { this.elasticsearchTemplate = elasticsearchTemplate; this.repository = repository; } @Override public Stream search(String query) { - NativeSearchQuery nativeSearchQuery = new NativeSearchQuery(queryStringQuery(query)); - return search(nativeSearchQuery); + NativeQuery nativeQuery = new NativeQuery(QueryStringQuery.of(qs -> qs.query(query))._toQuery()); + return search(nativeQuery); } @Override @@ -49,4 +53,9 @@ public Stream search(Query query) { public void index(Preferences entity) { repository.findOneWithEagerRelationships(entity.getId()).ifPresent(elasticsearchTemplate::save); } + + @Override + public void deleteFromIndexById(Long id) { + elasticsearchTemplate.delete(String.valueOf(id), Preferences.class); + } } diff --git a/src/main/java/org/jhipster/health/repository/search/UserSearchRepository.java b/src/main/java/org/jhipster/health/repository/search/UserSearchRepository.java index 5ff49497..251a3f5b 100644 --- a/src/main/java/org/jhipster/health/repository/search/UserSearchRepository.java +++ b/src/main/java/org/jhipster/health/repository/search/UserSearchRepository.java @@ -1,13 +1,15 @@ package org.jhipster.health.repository.search; -import static org.elasticsearch.index.query.QueryBuilders.queryStringQuery; - +import co.elastic.clients.elasticsearch._types.query_dsl.QueryStringQuery; import java.util.stream.Stream; import org.jhipster.health.domain.User; -import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate; +import org.jhipster.health.repository.UserRepository; +import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate; +import org.springframework.data.elasticsearch.client.elc.NativeQuery; import org.springframework.data.elasticsearch.core.SearchHit; -import org.springframework.data.elasticsearch.core.query.NativeSearchQuery; import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; +import org.springframework.scheduling.annotation.Async; +import org.springframework.transaction.annotation.Transactional; /** * Spring Data Elasticsearch repository for the User entity. @@ -16,19 +18,39 @@ public interface UserSearchRepository extends ElasticsearchRepository search(String query); + + @Async + @Transactional + void index(User entity); + + @Async + @Transactional + void deleteFromIndex(User entity); } class UserSearchRepositoryInternalImpl implements UserSearchRepositoryInternal { - private final ElasticsearchRestTemplate elasticsearchTemplate; + private final ElasticsearchTemplate elasticsearchTemplate; + private final UserRepository repository; - UserSearchRepositoryInternalImpl(ElasticsearchRestTemplate elasticsearchTemplate) { + UserSearchRepositoryInternalImpl(ElasticsearchTemplate elasticsearchTemplate, UserRepository repository) { this.elasticsearchTemplate = elasticsearchTemplate; + this.repository = repository; } @Override public Stream search(String query) { - NativeSearchQuery nativeSearchQuery = new NativeSearchQuery(queryStringQuery(query)); - return elasticsearchTemplate.search(nativeSearchQuery, User.class).map(SearchHit::getContent).stream(); + NativeQuery nativeQuery = new NativeQuery(QueryStringQuery.of(qs -> qs.query(query))._toQuery()); + return elasticsearchTemplate.search(nativeQuery, User.class).map(SearchHit::getContent).stream(); + } + + @Override + public void index(User entity) { + repository.findById(entity.getId()).ifPresent(elasticsearchTemplate::save); + } + + @Override + public void deleteFromIndex(User entity) { + elasticsearchTemplate.delete(entity); } } diff --git a/src/main/java/org/jhipster/health/repository/search/WeightSearchRepository.java b/src/main/java/org/jhipster/health/repository/search/WeightSearchRepository.java index 568717b5..1e25e423 100644 --- a/src/main/java/org/jhipster/health/repository/search/WeightSearchRepository.java +++ b/src/main/java/org/jhipster/health/repository/search/WeightSearchRepository.java @@ -1,21 +1,19 @@ package org.jhipster.health.repository.search; -import static org.elasticsearch.index.query.QueryBuilders.queryStringQuery; - -import java.util.List; +import co.elastic.clients.elasticsearch._types.query_dsl.QueryStringQuery; import java.util.List; -import java.util.stream.Collectors; import org.jhipster.health.domain.Weight; import org.jhipster.health.repository.WeightRepository; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.Pageable; -import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate; +import org.springframework.data.elasticsearch.client.elc.ElasticsearchTemplate; +import org.springframework.data.elasticsearch.client.elc.NativeQuery; import org.springframework.data.elasticsearch.core.SearchHit; import org.springframework.data.elasticsearch.core.SearchHits; -import org.springframework.data.elasticsearch.core.query.NativeSearchQuery; import org.springframework.data.elasticsearch.core.query.Query; import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; +import org.springframework.scheduling.annotation.Async; /** * Spring Data Elasticsearch repository for the {@link Weight} entity. @@ -27,29 +25,33 @@ interface WeightSearchRepositoryInternal { Page search(Query query); + @Async void index(Weight entity); + + @Async + void deleteFromIndexById(Long id); } class WeightSearchRepositoryInternalImpl implements WeightSearchRepositoryInternal { - private final ElasticsearchRestTemplate elasticsearchTemplate; + private final ElasticsearchTemplate elasticsearchTemplate; private final WeightRepository repository; - WeightSearchRepositoryInternalImpl(ElasticsearchRestTemplate elasticsearchTemplate, WeightRepository repository) { + WeightSearchRepositoryInternalImpl(ElasticsearchTemplate elasticsearchTemplate, WeightRepository repository) { this.elasticsearchTemplate = elasticsearchTemplate; this.repository = repository; } @Override public Page search(String query, Pageable pageable) { - NativeSearchQuery nativeSearchQuery = new NativeSearchQuery(queryStringQuery(query)); - return search(nativeSearchQuery.setPageable(pageable)); + NativeQuery nativeQuery = new NativeQuery(QueryStringQuery.of(qs -> qs.query(query))._toQuery()); + return search(nativeQuery.setPageable(pageable)); } @Override public Page search(Query query) { SearchHits searchHits = elasticsearchTemplate.search(query, Weight.class); - List hits = searchHits.map(SearchHit::getContent).stream().collect(Collectors.toList()); + List hits = searchHits.map(SearchHit::getContent).stream().toList(); return new PageImpl<>(hits, query.getPageable(), searchHits.getTotalHits()); } @@ -57,4 +59,9 @@ public Page search(Query query) { public void index(Weight entity) { repository.findOneWithEagerRelationships(entity.getId()).ifPresent(elasticsearchTemplate::save); } + + @Override + public void deleteFromIndexById(Long id) { + elasticsearchTemplate.delete(String.valueOf(id), Weight.class); + } } diff --git a/src/main/java/org/jhipster/health/repository/search/package-info.java b/src/main/java/org/jhipster/health/repository/search/package-info.java index 2655491b..fe4583e0 100644 --- a/src/main/java/org/jhipster/health/repository/search/package-info.java +++ b/src/main/java/org/jhipster/health/repository/search/package-info.java @@ -1,4 +1,4 @@ /** - * Spring Data Elasticsearch repositories. + * This package file was generated by JHipster */ package org.jhipster.health.repository.search; diff --git a/src/main/java/org/jhipster/health/security/DomainUserDetailsService.java b/src/main/java/org/jhipster/health/security/DomainUserDetailsService.java index 97be3c28..b6862ec7 100644 --- a/src/main/java/org/jhipster/health/security/DomainUserDetailsService.java +++ b/src/main/java/org/jhipster/health/security/DomainUserDetailsService.java @@ -1,14 +1,12 @@ package org.jhipster.health.security; import java.util.*; -import java.util.stream.Collectors; import org.hibernate.validator.internal.constraintvalidators.hv.EmailValidator; import org.jhipster.health.domain.Authority; import org.jhipster.health.domain.User; import org.jhipster.health.repository.UserRepository; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; @@ -53,12 +51,12 @@ private org.springframework.security.core.userdetails.User createSpringSecurityU if (!user.isActivated()) { throw new UserNotActivatedException("User " + lowercaseLogin + " was not activated"); } - List grantedAuthorities = user + List grantedAuthorities = user .getAuthorities() .stream() .map(Authority::getName) .map(SimpleGrantedAuthority::new) - .collect(Collectors.toList()); + .toList(); return new org.springframework.security.core.userdetails.User(user.getLogin(), user.getPassword(), grantedAuthorities); } } diff --git a/src/main/java/org/jhipster/health/security/SecurityUtils.java b/src/main/java/org/jhipster/health/security/SecurityUtils.java index 181d9c37..a86df3d1 100644 --- a/src/main/java/org/jhipster/health/security/SecurityUtils.java +++ b/src/main/java/org/jhipster/health/security/SecurityUtils.java @@ -8,12 +8,18 @@ import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.oauth2.jose.jws.MacAlgorithm; +import org.springframework.security.oauth2.jwt.Jwt; /** * Utility class for Spring Security. */ public final class SecurityUtils { + public static final MacAlgorithm JWT_ALGORITHM = MacAlgorithm.HS512; + + public static final String AUTHORITIES_KEY = "auth"; + private SecurityUtils() {} /** @@ -29,11 +35,12 @@ public static Optional getCurrentUserLogin() { private static String extractPrincipal(Authentication authentication) { if (authentication == null) { return null; - } else if (authentication.getPrincipal() instanceof UserDetails) { - UserDetails springSecurityUser = (UserDetails) authentication.getPrincipal(); + } else if (authentication.getPrincipal() instanceof UserDetails springSecurityUser) { return springSecurityUser.getUsername(); - } else if (authentication.getPrincipal() instanceof String) { - return (String) authentication.getPrincipal(); + } else if (authentication.getPrincipal() instanceof Jwt jwt) { + return jwt.getSubject(); + } else if (authentication.getPrincipal() instanceof String s) { + return s; } return null; } diff --git a/src/main/java/org/jhipster/health/security/jwt/JWTConfigurer.java b/src/main/java/org/jhipster/health/security/jwt/JWTConfigurer.java deleted file mode 100644 index f604f6e0..00000000 --- a/src/main/java/org/jhipster/health/security/jwt/JWTConfigurer.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.jhipster.health.security.jwt; - -import org.springframework.security.config.annotation.SecurityConfigurerAdapter; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.web.DefaultSecurityFilterChain; -import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; - -public class JWTConfigurer extends SecurityConfigurerAdapter { - - private final TokenProvider tokenProvider; - - public JWTConfigurer(TokenProvider tokenProvider) { - this.tokenProvider = tokenProvider; - } - - @Override - public void configure(HttpSecurity http) { - JWTFilter customFilter = new JWTFilter(tokenProvider); - http.addFilterBefore(customFilter, UsernamePasswordAuthenticationFilter.class); - } -} diff --git a/src/main/java/org/jhipster/health/security/jwt/JWTFilter.java b/src/main/java/org/jhipster/health/security/jwt/JWTFilter.java deleted file mode 100644 index 4566b056..00000000 --- a/src/main/java/org/jhipster/health/security/jwt/JWTFilter.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.jhipster.health.security.jwt; - -import java.io.IOException; -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.util.StringUtils; -import org.springframework.web.filter.GenericFilterBean; - -/** - * Filters incoming requests and installs a Spring Security principal if a header corresponding to a valid user is - * found. - */ -public class JWTFilter extends GenericFilterBean { - - public static final String AUTHORIZATION_HEADER = "Authorization"; - - private final TokenProvider tokenProvider; - - public JWTFilter(TokenProvider tokenProvider) { - this.tokenProvider = tokenProvider; - } - - @Override - public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) - throws IOException, ServletException { - HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; - String jwt = resolveToken(httpServletRequest); - if (StringUtils.hasText(jwt) && this.tokenProvider.validateToken(jwt)) { - Authentication authentication = this.tokenProvider.getAuthentication(jwt); - SecurityContextHolder.getContext().setAuthentication(authentication); - } - filterChain.doFilter(servletRequest, servletResponse); - } - - private String resolveToken(HttpServletRequest request) { - String bearerToken = request.getHeader(AUTHORIZATION_HEADER); - if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) { - return bearerToken.substring(7); - } - return null; - } -} diff --git a/src/main/java/org/jhipster/health/security/jwt/TokenProvider.java b/src/main/java/org/jhipster/health/security/jwt/TokenProvider.java deleted file mode 100644 index 4b3ae7cf..00000000 --- a/src/main/java/org/jhipster/health/security/jwt/TokenProvider.java +++ /dev/null @@ -1,124 +0,0 @@ -package org.jhipster.health.security.jwt; - -import io.jsonwebtoken.*; -import io.jsonwebtoken.io.Decoders; -import io.jsonwebtoken.security.Keys; -import io.jsonwebtoken.security.SignatureException; -import java.nio.charset.StandardCharsets; -import java.security.Key; -import java.util.*; -import java.util.stream.Collectors; -import org.jhipster.health.management.SecurityMetersService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.userdetails.User; -import org.springframework.stereotype.Component; -import org.springframework.util.ObjectUtils; -import tech.jhipster.config.JHipsterProperties; - -@Component -public class TokenProvider { - - private final Logger log = LoggerFactory.getLogger(TokenProvider.class); - - private static final String AUTHORITIES_KEY = "auth"; - - private static final String INVALID_JWT_TOKEN = "Invalid JWT token."; - - private final Key key; - - private final JwtParser jwtParser; - - private final long tokenValidityInMilliseconds; - - private final long tokenValidityInMillisecondsForRememberMe; - - private final SecurityMetersService securityMetersService; - - public TokenProvider(JHipsterProperties jHipsterProperties, SecurityMetersService securityMetersService) { - byte[] keyBytes; - String secret = jHipsterProperties.getSecurity().getAuthentication().getJwt().getBase64Secret(); - if (!ObjectUtils.isEmpty(secret)) { - log.debug("Using a Base64-encoded JWT secret key"); - keyBytes = Decoders.BASE64.decode(secret); - } else { - log.warn( - "Warning: the JWT key used is not Base64-encoded. " + - "We recommend using the `jhipster.security.authentication.jwt.base64-secret` key for optimum security." - ); - secret = jHipsterProperties.getSecurity().getAuthentication().getJwt().getSecret(); - keyBytes = secret.getBytes(StandardCharsets.UTF_8); - } - key = Keys.hmacShaKeyFor(keyBytes); - jwtParser = Jwts.parserBuilder().setSigningKey(key).build(); - this.tokenValidityInMilliseconds = 1000 * jHipsterProperties.getSecurity().getAuthentication().getJwt().getTokenValidityInSeconds(); - this.tokenValidityInMillisecondsForRememberMe = 1000 * - jHipsterProperties.getSecurity().getAuthentication().getJwt().getTokenValidityInSecondsForRememberMe(); - - this.securityMetersService = securityMetersService; - } - - public String createToken(Authentication authentication, boolean rememberMe) { - String authorities = authentication.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.joining(",")); - - long now = (new Date()).getTime(); - Date validity; - if (rememberMe) { - validity = new Date(now + this.tokenValidityInMillisecondsForRememberMe); - } else { - validity = new Date(now + this.tokenValidityInMilliseconds); - } - - return Jwts.builder() - .setSubject(authentication.getName()) - .claim(AUTHORITIES_KEY, authorities) - .signWith(key, SignatureAlgorithm.HS512) - .setExpiration(validity) - .compact(); - } - - public Authentication getAuthentication(String token) { - Claims claims = jwtParser.parseClaimsJws(token).getBody(); - - Collection authorities = Arrays.stream(claims.get(AUTHORITIES_KEY).toString().split(",")) - .filter(auth -> !auth.trim().isEmpty()) - .map(SimpleGrantedAuthority::new) - .collect(Collectors.toList()); - - User principal = new User(claims.getSubject(), "", authorities); - - return new UsernamePasswordAuthenticationToken(principal, token, authorities); - } - - public boolean validateToken(String authToken) { - try { - jwtParser.parseClaimsJws(authToken); - - return true; - } catch (ExpiredJwtException e) { - this.securityMetersService.trackTokenExpired(); - - log.trace(INVALID_JWT_TOKEN, e); - } catch (UnsupportedJwtException e) { - this.securityMetersService.trackTokenUnsupported(); - - log.trace(INVALID_JWT_TOKEN, e); - } catch (MalformedJwtException e) { - this.securityMetersService.trackTokenMalformed(); - - log.trace(INVALID_JWT_TOKEN, e); - } catch (SignatureException e) { - this.securityMetersService.trackTokenInvalidSignature(); - - log.trace(INVALID_JWT_TOKEN, e); - } catch (IllegalArgumentException e) { // TODO: should we let it bubble (no catch), to avoid defensive programming and follow the fail-fast principle? - log.error("Token validation error {}", e.getMessage()); - } - - return false; - } -} diff --git a/src/main/java/org/jhipster/health/security/package-info.java b/src/main/java/org/jhipster/health/security/package-info.java index 3eaab12e..9cc0d099 100644 --- a/src/main/java/org/jhipster/health/security/package-info.java +++ b/src/main/java/org/jhipster/health/security/package-info.java @@ -1,4 +1,4 @@ /** - * Spring Security configuration. + * Application security utilities. */ package org.jhipster.health.security; diff --git a/src/main/java/org/jhipster/health/service/ElasticsearchIndexService.java b/src/main/java/org/jhipster/health/service/ElasticsearchIndexService.java deleted file mode 100644 index 56670987..00000000 --- a/src/main/java/org/jhipster/health/service/ElasticsearchIndexService.java +++ /dev/null @@ -1,198 +0,0 @@ -package org.jhipster.health.service; - -import com.codahale.metrics.annotation.Timed; -import com.fasterxml.jackson.annotation.JsonIgnore; -import java.beans.IntrospectionException; -import java.beans.PropertyDescriptor; -import java.io.Serializable; -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; -import java.util.Set; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; -import java.util.stream.Collectors; -import javax.persistence.ManyToMany; -import org.elasticsearch.ResourceAlreadyExistsException; -import org.jhipster.health.config.ApplicationProperties; -import org.jhipster.health.domain.*; -import org.jhipster.health.repository.*; -import org.jhipster.health.repository.search.*; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.context.ApplicationListener; -import org.springframework.context.event.ContextRefreshedEvent; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageRequest; -import org.springframework.data.domain.Pageable; -import org.springframework.data.elasticsearch.core.ElasticsearchOperations; -import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.scheduling.annotation.Async; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -@Service -@Transactional(readOnly = true) -public class ElasticsearchIndexService implements ApplicationListener { - - private static final Lock reindexLock = new ReentrantLock(); - - private final Logger log = LoggerFactory.getLogger(ElasticsearchIndexService.class); - - private final PointsRepository pointsRepository; - - private final PointsSearchRepository pointsSearchRepository; - - private final WeightRepository weightRepository; - - private final WeightSearchRepository weightSearchRepository; - - private final BloodPressureRepository bloodPressureRepository; - - private final BloodPressureSearchRepository bloodPressureSearchRepository; - - private final PreferencesRepository preferencesRepository; - - private final PreferencesSearchRepository preferencesSearchRepository; - - private final UserRepository userRepository; - - private final UserSearchRepository userSearchRepository; - - private final ElasticsearchOperations elasticsearchOperations; - - private final ApplicationProperties applicationProperties; - - private boolean reindex_elasticsearch; - - public ElasticsearchIndexService( - UserRepository userRepository, - UserSearchRepository userSearchRepository, - PointsRepository pointsRepository, - PointsSearchRepository pointsSearchRepository, - WeightRepository weightRepository, - WeightSearchRepository weightSearchRepository, - BloodPressureRepository bloodPressureRepository, - BloodPressureSearchRepository bloodPressureSearchRepository, - PreferencesRepository preferencesRepository, - PreferencesSearchRepository preferencesSearchRepository, - ElasticsearchOperations elasticsearchOperations, - ApplicationProperties applicationProperties - ) { - this.userRepository = userRepository; - this.userSearchRepository = userSearchRepository; - this.pointsRepository = pointsRepository; - this.pointsSearchRepository = pointsSearchRepository; - this.weightRepository = weightRepository; - this.weightSearchRepository = weightSearchRepository; - this.bloodPressureRepository = bloodPressureRepository; - this.bloodPressureSearchRepository = bloodPressureSearchRepository; - this.preferencesRepository = preferencesRepository; - this.preferencesSearchRepository = preferencesSearchRepository; - this.elasticsearchOperations = elasticsearchOperations; - this.applicationProperties = applicationProperties; - } - - public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) { - this.reindex_elasticsearch = applicationProperties.getElasticsearch().getReindexOnStartup(); - if (this.reindex_elasticsearch) { - log.info("Reindexing all entities with Elasticsearch..."); - this.reindexSelected(null, true); - } else { - log.info( - "Skipping Elasticsearch reindex. You may reindex all entities with Elasticsearch on start up by setting application.elasticsearch.reindex-on-startup to true." - ); - } - } - - @Async - @Timed - public void reindexSelected(List classesForReindex, boolean all) { - if (reindexLock.tryLock()) { - try { - if (all || classesForReindex.contains("Points")) { - reindexForClass(Points.class, pointsRepository, pointsSearchRepository); - } - if (all || classesForReindex.contains("Weight")) { - reindexForClass(Weight.class, weightRepository, weightSearchRepository); - } - if (all || classesForReindex.contains("BloodPressure")) { - reindexForClass(BloodPressure.class, bloodPressureRepository, bloodPressureSearchRepository); - } - if (all || classesForReindex.contains("Preferences")) { - reindexForClass(Preferences.class, preferencesRepository, preferencesSearchRepository); - } - if (all || classesForReindex.contains("User")) { - reindexForClass(User.class, userRepository, userSearchRepository); - } - - log.info("Elasticsearch: Successfully performed reindexing"); - } finally { - reindexLock.unlock(); - } - } else { - log.info("Elasticsearch: concurrent reindexing attempt"); - } - } - - @SuppressWarnings("unchecked") - private void reindexForClass( - Class entityClass, - JpaRepository jpaRepository, - ElasticsearchRepository elasticsearchRepository - ) { - String className = entityClass.getSimpleName(); - elasticsearchOperations.indexOps(entityClass).delete(); - try { - elasticsearchOperations.indexOps(entityClass).createWithMapping(); - } catch (ResourceAlreadyExistsException e) { - // Do nothing. Index was already concurrently recreated by some other service. - } - if (jpaRepository.count() > 0) { - // if a JHipster entity field is the owner side of a many-to-many relationship, it should be loaded manually - List relationshipGetters = Arrays.stream(entityClass.getDeclaredFields()) - .filter(field -> field.getType().equals(Set.class)) - .filter(field -> field.getAnnotation(ManyToMany.class) != null) - .filter(field -> field.getAnnotation(ManyToMany.class).mappedBy().isEmpty()) - .filter(field -> field.getAnnotation(JsonIgnore.class) == null) - .map(field -> { - try { - return new PropertyDescriptor(field.getName(), entityClass).getReadMethod(); - } catch (IntrospectionException e) { - log.error( - "Error retrieving getter for class {}, field {}. Field will NOT be indexed", - className, - field.getName(), - e - ); - return null; - } - }) - .filter(Objects::nonNull) - .collect(Collectors.toList()); - - int size = 100; - for (int i = 0; i <= jpaRepository.count() / size; i++) { - Pageable page = PageRequest.of(i, size); - log.info("Indexing {} page {} of {}, size {}", className, i, jpaRepository.count() / size, size); - Page results = jpaRepository.findAll(page); - results.map(result -> { - // if there are any relationships to load, do it now - relationshipGetters.forEach(method -> { - try { - // eagerly load the relationship set - ((Set) method.invoke(result)).size(); - } catch (Exception ex) { - log.error(ex.getMessage()); - } - }); - return result; - }); - elasticsearchRepository.saveAll(results.getContent()); - } - } - log.info("Elasticsearch: Indexed all rows for {}", className); - } -} diff --git a/src/main/java/org/jhipster/health/service/MailService.java b/src/main/java/org/jhipster/health/service/MailService.java index e0454fa4..ffe1e8d3 100644 --- a/src/main/java/org/jhipster/health/service/MailService.java +++ b/src/main/java/org/jhipster/health/service/MailService.java @@ -1,9 +1,9 @@ package org.jhipster.health.service; +import jakarta.mail.MessagingException; +import jakarta.mail.internet.MimeMessage; import java.nio.charset.StandardCharsets; import java.util.Locale; -import javax.mail.MessagingException; -import javax.mail.internet.MimeMessage; import org.jhipster.health.domain.User; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -14,11 +14,11 @@ import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; import org.thymeleaf.context.Context; -import org.thymeleaf.spring5.SpringTemplateEngine; +import org.thymeleaf.spring6.SpringTemplateEngine; import tech.jhipster.config.JHipsterProperties; /** - * Service for sending emails. + * Service for sending emails asynchronously. *