From b1e3353a1d6a2a0e858f42581b989e1fb7780851 Mon Sep 17 00:00:00 2001 From: death-claw <53543762+death-claw@users.noreply.github.com> Date: Sat, 23 Jul 2022 16:42:52 +0100 Subject: [PATCH] v4.0.0 --- .github/workflows/maven-publish.yml | 34 + CHANGELOG.md | 9 + README.md | 32 +- pom.xml | 33 +- {vripper-server => vripper-core}/.gitignore | 0 .../.mvn/wrapper/maven-wrapper.jar | Bin .../.mvn/wrapper/maven-wrapper.properties | 0 {vripper-server => vripper-core}/mvnw | 0 {vripper-server => vripper-core}/mvnw.cmd | 0 vripper-core/pom.xml | 128 + .../me/mnlr/vripper/AppEndpointService.kt | 111 + .../me/mnlr/vripper/EventListenerBean.kt | 19 + .../main/kotlin/me/mnlr/vripper/Management.kt | 58 + .../kotlin/me/mnlr/vripper/SpringContext.kt | 37 + .../src/main/kotlin/me/mnlr/vripper/Utils.kt | 39 + .../mnlr/vripper/delegate/LoggerDelegate.kt | 16 + .../mnlr/vripper/download/DownloadService.kt | 297 + .../vripper/download/ImageDownloadContext.kt | 25 + .../vripper/download/ImageDownloadRunnable.kt | 178 + .../vripper/download/PostDownloadRunnable.kt | 162 + .../vripper/entities/ImageDownloadState.kt | 31 + .../me/mnlr/vripper/entities/LogEvent.kt | 22 + .../me/mnlr/vripper/entities/Metadata.kt | 19 + .../vripper/entities/PostDownloadState.kt | 37 + .../kotlin/me/mnlr/vripper/entities/Thread.kt | 21 + .../me/mnlr/vripper/entities/domain/Status.kt | 5 + .../kotlin/me/mnlr/vripper/event/Event.kt | 7 + .../kotlin/me/mnlr/vripper/event/EventBus.kt | 30 + .../vripper/exception/DownloadException.kt | 6 + .../mnlr/vripper/exception/HostException.kt | 7 + .../exception/HtmlProcessorException.kt | 3 + .../vripper/exception/PostParseException.kt | 7 + .../mnlr/vripper/exception/QueueException.kt | 7 + .../mnlr/vripper/exception/RenameException.kt | 3 + .../vripper/exception/ValidationException.kt | 3 + .../vripper/exception/VripperException.kt | 6 + .../mnlr/vripper/exception/XpathException.kt | 3 + .../me/mnlr/vripper/host/AcidimgHost.kt | 104 + .../kotlin/me/mnlr/vripper/host/DPicMeHost.kt | 69 + .../main/kotlin/me/mnlr/vripper/host/Host.kt | 229 + .../me/mnlr/vripper/host/ImageBamHost.kt | 92 + .../me/mnlr/vripper/host/ImageTwistHost.kt | 61 + .../me/mnlr/vripper/host/ImageVenueHost.kt | 80 + .../me/mnlr/vripper/host/ImageZillaHost.kt | 59 + .../me/mnlr/vripper/host/ImgSpiceHost.kt | 59 + .../kotlin/me/mnlr/vripper/host/ImgboxHost.kt | 57 + .../kotlin/me/mnlr/vripper/host/ImxHost.kt | 107 + .../me/mnlr/vripper/host/PimpandhostHost.kt | 85 + .../me/mnlr/vripper/host/PixRouteHost.kt | 56 + .../me/mnlr/vripper/host/PixhostHost.kt | 57 + .../me/mnlr/vripper/host/PixxxelsHost.kt | 71 + .../me/mnlr/vripper/host/PostImgHost.kt | 72 + .../me/mnlr/vripper/host/TurboImageHost.kt | 59 + .../kotlin/me/mnlr/vripper/host/ViprImHost.kt | 58 + .../me/mnlr/vripper/model/DownloadSpeed.kt | 19 + .../me/mnlr/vripper/model/GlobalState.kt | 33 + .../kotlin/me/mnlr/vripper/model/ImageItem.kt | 5 + .../me/mnlr/vripper/model/LoggedUser.kt | 3 + .../kotlin/me/mnlr/vripper/model/PostItem.kt | 17 + .../kotlin/me/mnlr/vripper/model/Settings.kt | 35 + .../me/mnlr/vripper/model/ThreadInput.kt | 3 + .../me/mnlr/vripper/model/ThreadItem.kt | 9 + .../vripper/parser/ThreadLookupAPIParser.kt | 79 + .../parser/ThreadLookupAPIResponseHandler.kt | 104 + .../vripper/repositories/ImageRepository.kt | 16 + .../repositories/LogEventRepository.kt | 13 + .../repositories/MetadataRepository.kt | 10 + .../PostDownloadStateRepository.kt | 16 + .../vripper/repositories/ThreadRepository.kt | 13 + .../repositories/impl/ImageRepositoryImpl.kt | 125 + .../impl/LogEventRepositoryImpl.kt | 107 + .../impl/MetadataRepositoryImpl.kt | 65 + .../impl/PostDownloadStateRepositoryImpl.kt | 163 + .../repositories/impl/ThreadRepositoryImpl.kt | 87 + .../mnlr/vripper/services/DataTransaction.kt | 149 + .../vripper/services/DownloadSpeedService.kt | 30 + .../vripper/services/GlobalStateService.kt | 44 + .../me/mnlr/vripper/services/HTTPService.kt | 131 + .../vripper/services/HtmlProcessorService.kt | 24 + .../mnlr/vripper/services/MetadataService.kt | 38 + .../me/mnlr/vripper/services/PathService.kt | 49 + .../vripper/services/RetryPolicyService.kt | 54 + .../mnlr/vripper/services/SettingsService.kt | 211 + .../vripper/services/ThreadCacheService.kt | 39 + .../vripper/services/ThreadPoolService.kt | 17 + .../me/mnlr/vripper/services/VGAuthService.kt | 142 + .../me/mnlr/vripper/services/XpathService.kt | 31 + .../mnlr/vripper/tasks/LeaveThanksRunnable.kt | 118 + .../me/mnlr/vripper/tasks/LinkScanRunnable.kt | 110 + .../me/mnlr/vripper/tasks/MetadataRunnable.kt | 177 + .../vripper/tasks/ThreadLookupRunnable.kt | 106 + .../src/main/resources/application.properties | 2 +- .../db/changelog/db.changelog-master.xml | 145 + .../src/main/resources/logback-spring.xml | 24 + .../src/main/resources/proxies.json | 0 vripper-electron/.gitignore | 47 - vripper-electron/.jshintrc | 7 - vripper-electron/build/icon.icns | Bin 43326 -> 0 bytes vripper-electron/build/icon.ico | Bin 310268 -> 0 bytes vripper-electron/build/icon.png | Bin 2103 -> 0 bytes vripper-electron/build/icons/1024x1024.png | Bin 39289 -> 0 bytes vripper-electron/build/icons/128x128.png | Bin 4049 -> 0 bytes vripper-electron/build/icons/16x16.png | Bin 592 -> 0 bytes vripper-electron/build/icons/256x256.png | Bin 8456 -> 0 bytes vripper-electron/build/icons/32x32.png | Bin 1092 -> 0 bytes vripper-electron/build/icons/48x48.png | Bin 1578 -> 0 bytes vripper-electron/build/icons/512x512.png | Bin 17957 -> 0 bytes vripper-electron/build/icons/64x64.png | Bin 2103 -> 0 bytes vripper-electron/main.js | 182 - vripper-electron/package.json | 84 - vripper-electron/pom.xml | 77 - vripper-electron/pre-build.js | 25 - vripper-electron/renderer.js | 3 - vripper-electron/vripper-electron.iml | 12 - vripper-electron/yarn.lock | 1888 ---- vripper-gui/.gitignore | 33 + vripper-gui/.mvn/wrapper/maven-wrapper.jar | Bin 0 -> 58727 bytes .../.mvn/wrapper/maven-wrapper.properties | 2 + vripper-gui/mvnw | 316 + vripper-gui/mvnw.cmd | 188 + vripper-gui/pom.xml | 154 + .../me/mnlr/vripper/VripperGuiApplication.kt | 76 + .../controller/GlobalStateController.kt | 62 + .../vripper/controller/ImageController.kt | 31 + .../mnlr/vripper/controller/LogController.kt | 37 + .../mnlr/vripper/controller/PostController.kt | 69 + .../vripper/controller/SettingsController.kt | 69 + .../vripper/controller/ThreadController.kt | 61 + .../vripper/event/ApplicationInitialized.kt | 6 + .../main/kotlin/me/mnlr/vripper/gui/Styles.kt | 30 + .../me/mnlr/vripper/model/GlobalStateModel.kt | 29 + .../me/mnlr/vripper/model/ImageModel.kt | 31 + .../kotlin/me/mnlr/vripper/model/LogModel.kt | 28 + .../kotlin/me/mnlr/vripper/model/PostModel.kt | 53 + .../me/mnlr/vripper/model/ThreadModel.kt | 17 + .../vripper/model/ThreadSelectionModel.kt | 27 + .../model/settings/ConnectionSettingsModel.kt | 18 + .../model/settings/DownloadSettingsModel.kt | 30 + .../model/settings/ViperSettingsModel.kt | 21 + .../kotlin/me/mnlr/vripper/view/AppView.kt | 17 + .../me/mnlr/vripper/view/LoadingView.kt | 30 + .../vripper/view/actionbar/ActionBarView.kt | 29 + .../vripper/view/actionbar/AddActionsView.kt | 31 + .../view/actionbar/DownloadActionsView.kt | 134 + .../vripper/view/actionbar/LogActionsView.kt | 36 + .../view/actionbar/ThreadActionsView.kt | 54 + .../me/mnlr/vripper/view/main/MainView.kt | 74 + .../me/mnlr/vripper/view/posts/AddView.kt | 36 + .../view/settings/ConnectionSettingsView.kt | 45 + .../view/settings/DownloadSettingsView.kt | 79 + .../vripper/view/settings/SettingsView.kt | 71 + .../view/settings/ViperSettingsView.kt | 52 + .../mnlr/vripper/view/status/StatusBarView.kt | 36 + .../vripper/view/tables/ImagesTableView.kt | 97 + .../mnlr/vripper/view/tables/LogTableView.kt | 64 + .../vripper/view/tables/PostsTableView.kt | 178 + .../view/tables/ThreadSelectionTableView.kt | 73 + .../vripper/view/tables/ThreadTableView.kt | 110 + vripper-gui/src/main/resources/analyze.png | Bin 0 -> 918 bytes vripper-gui/src/main/resources/broom.png | Bin 0 -> 1199 bytes vripper-gui/src/main/resources/bug.png | Bin 0 -> 1563 bytes .../src/main/resources/data-transfer.png | Bin 0 -> 471 bytes vripper-gui/src/main/resources/details.png | Bin 0 -> 605 bytes vripper-gui/src/main/resources/download.png | Bin 0 -> 388 bytes .../src/main/resources/downloads-folder.png | Bin 0 -> 481 bytes vripper-gui/src/main/resources/end.png | Bin 0 -> 285 bytes .../src/main/resources/icons/1024x1024.png | Bin 0 -> 40330 bytes .../src/main/resources/icons/128x128.png | Bin 0 -> 3802 bytes .../src/main/resources/icons/16x16.png | Bin 0 -> 478 bytes .../src/main/resources/icons/256x256.png | Bin 0 -> 8148 bytes .../src/main/resources/icons/32x32.png | Bin 0 -> 948 bytes .../src/main/resources/icons/48x48.png | Bin 0 -> 1423 bytes .../src/main/resources/icons/512x512.png | Bin 0 -> 18123 bytes .../src/main/resources/icons/64x64.png | Bin 0 -> 1880 bytes vripper-gui/src/main/resources/pause.png | Bin 0 -> 154 bytes vripper-gui/src/main/resources/play.png | Bin 0 -> 218 bytes vripper-gui/src/main/resources/plus.png | Bin 0 -> 677 bytes vripper-gui/src/main/resources/popup.png | Bin 0 -> 332 bytes vripper-gui/src/main/resources/rename.png | Bin 0 -> 388 bytes vripper-gui/src/main/resources/save.png | Bin 0 -> 314 bytes vripper-gui/src/main/resources/search.png | Bin 0 -> 1116 bytes vripper-gui/src/main/resources/settings.png | Bin 0 -> 1279 bytes vripper-gui/src/main/resources/stop.png | Bin 0 -> 147 bytes vripper-gui/src/main/resources/trash.png | Bin 0 -> 376 bytes vripper-server/pom.xml | 160 - .../tn/mnlr/vripper/EventListenerBean.java | 31 - .../java/tn/mnlr/vripper/SpringContext.java | 39 - .../src/main/java/tn/mnlr/vripper/Utils.java | 24 - .../tn/mnlr/vripper/VripperApplication.java | 19 - .../tn/mnlr/vripper/download/DownloadJob.java | 295 - .../vripper/download/DownloadJobWrapper.java | 70 - .../vripper/download/DownloadService.java | 274 - .../java/tn/mnlr/vripper/event/Event.java | 34 - .../java/tn/mnlr/vripper/event/EventBus.java | 27 - .../vripper/exception/DownloadException.java | 12 - .../mnlr/vripper/exception/HostException.java | 15 - .../exception/HtmlProcessorException.java | 8 - .../vripper/exception/PostParseException.java | 16 - .../vripper/exception/QueueException.java | 16 - .../vripper/exception/RenameException.java | 10 - .../exception/ValidationException.java | 8 - .../vripper/exception/VripperException.java | 11 - .../vripper/exception/XpathException.java | 7 - .../tn/mnlr/vripper/host/AcidimgHost.java | 122 - .../java/tn/mnlr/vripper/host/DPicMeHost.java | 85 - .../main/java/tn/mnlr/vripper/host/Host.java | 43 - .../tn/mnlr/vripper/host/ImageBamHost.java | 96 - .../tn/mnlr/vripper/host/ImageTwistHost.java | 70 - .../tn/mnlr/vripper/host/ImageVenueHost.java | 84 - .../tn/mnlr/vripper/host/ImageZillaHost.java | 71 - .../tn/mnlr/vripper/host/ImgSpiceHost.java | 67 - .../java/tn/mnlr/vripper/host/ImgboxHost.java | 64 - .../java/tn/mnlr/vripper/host/ImxHost.java | 129 - .../tn/mnlr/vripper/host/PimpandhostHost.java | 75 - .../tn/mnlr/vripper/host/PixRouteHost.java | 69 - .../tn/mnlr/vripper/host/PixhostHost.java | 65 - .../tn/mnlr/vripper/host/PixxxelsHost.java | 72 - .../tn/mnlr/vripper/host/PostImgHost.java | 74 - .../tn/mnlr/vripper/host/TurboImageHost.java | 73 - .../java/tn/mnlr/vripper/host/ViprImHost.java | 70 - .../java/tn/mnlr/vripper/jpa/Management.java | 79 - .../jpa/domain/DateTimeSerializer.java | 25 - .../tn/mnlr/vripper/jpa/domain/Image.java | 66 - .../tn/mnlr/vripper/jpa/domain/LogEvent.java | 52 - .../tn/mnlr/vripper/jpa/domain/Metadata.java | 31 - .../java/tn/mnlr/vripper/jpa/domain/Post.java | 90 - .../tn/mnlr/vripper/jpa/domain/Queued.java | 56 - .../mnlr/vripper/jpa/domain/enums/Status.java | 10 - .../jpa/repositories/IImageRepository.java | 32 - .../jpa/repositories/ILogEventRepository.java | 21 - .../jpa/repositories/IMetadataRepository.java | 14 - .../jpa/repositories/IPostRepository.java | 38 - .../jpa/repositories/IQueuedRepository.java | 20 - .../vripper/jpa/repositories/IRepository.java | 3 - .../repositories/impl/ImageRepository.java | 156 - .../repositories/impl/LogEventRepository.java | 125 - .../repositories/impl/MetadataRepository.java | 76 - .../jpa/repositories/impl/PostRepository.java | 230 - .../repositories/impl/QueuedRepository.java | 107 - .../vripper/services/ConnectionService.java | 154 - .../tn/mnlr/vripper/services/DataService.java | 244 - .../services/DownloadSpeedService.java | 41 - .../vripper/services/GlobalStateService.java | 42 - .../tn/mnlr/vripper/services/HostService.java | 113 - .../services/HtmlProcessorService.java | 22 - .../vripper/services/MetadataService.java | 50 - .../tn/mnlr/vripper/services/PathService.java | 126 - .../tn/mnlr/vripper/services/PostService.java | 73 - .../vripper/services/SettingsService.java | 305 - .../vripper/services/ThreadPoolService.java | 20 - .../mnlr/vripper/services/VGAuthService.java | 159 - .../mnlr/vripper/services/XpathService.java | 32 - .../services/domain/DownloadSpeed.java | 40 - .../vripper/services/domain/GlobalState.java | 32 - .../services/domain/MultiPostItem.java | 36 - .../services/domain/MultiPostScanHandler.java | 114 - .../services/domain/MultiPostScanParser.java | 91 - .../services/domain/MultiPostScanResult.java | 16 - .../services/domain/PostScanHandler.java | 116 - .../services/domain/PostScanParser.java | 86 - .../services/domain/PostScanResult.java | 24 - .../vripper/services/domain/Settings.java | 108 - .../mnlr/vripper/tasks/AddPostRunnable.java | 138 - .../mnlr/vripper/tasks/AddQueuedRunnable.java | 103 - .../vripper/tasks/LeaveThanksRunnable.java | 134 - .../mnlr/vripper/tasks/LinkScanRunnable.java | 109 - .../mnlr/vripper/tasks/MetadataRunnable.java | 246 - .../restendpoints/EventLogRestEndpoint.java | 27 - .../web/restendpoints/PostRestEndpoint.java | 268 - .../restendpoints/SettingsRestEndpoint.java | 66 - .../web/restendpoints/WebSecurityConfig.java | 14 - .../web/restendpoints/domain/AltPostName.java | 15 - .../restendpoints/domain/DownloadPath.java | 18 - .../web/restendpoints/domain/PostId.java | 18 - .../web/restendpoints/domain/PostToAdd.java | 15 - .../restendpoints/domain/RemoveAllResult.java | 22 - .../restendpoints/domain/RemoveResult.java | 18 - .../web/restendpoints/domain/ThreadId.java | 14 - .../web/restendpoints/domain/ThreadUrl.java | 14 - .../web/wsendpoints/DataBroadcast.java | 158 - .../web/wsendpoints/DataController.java | 89 - .../web/wsendpoints/WebSocketConfig.java | 23 - .../db/changelog/db.changelog-master.xml | 178 - .../src/main/resources/logback-spring.xml | 23 - vripper-ui/e2e/protractor.conf.js | 28 - vripper-ui/e2e/src/app.e2e-spec.ts | 23 - vripper-ui/e2e/src/app.po.ts | 11 - vripper-ui/e2e/tsconfig.e2e.json | 13 - vripper-ui/package.json | 59 - .../about-component.component-scss-theme.scss | 10 - .../src/app/app.component.scss-theme.scss | 11 - .../src/app/domain/download-speed.model.ts | 4 - .../src/app/domain/logged-user.model.ts | 3 - vripper-ui/src/app/domain/settings.model.ts | 17 - .../app/event-log/event-log.component.html | 4 - .../multi-post-grid.component.html | 4 - .../alternative-title.component.html | 26 - .../alternative-title.component.scss | 7 - .../alternative-title.component.ts | 57 - vripper-ui/src/app/posts/posts.component.html | 4 - .../src/app/services/clipboard.service.ts | 72 - .../status-bar.component.scss-theme.scss | 11 - .../toolbar/toolbar.component.scss-theme.scss | 65 - vripper-ui/src/dark-theme.scss | 9 - vripper-ui/src/grid-theme.scss | 166 - vripper-ui/src/karma.conf.js | 31 - vripper-ui/src/light-theme.scss | 9 - vripper-ui/src/mat-theme.scss | 11 - vripper-ui/src/polyfills.ts | 85 - vripper-ui/src/test.ts | 17 - vripper-ui/src/tsconfig.app.json | 11 - vripper-ui/src/tsconfig.spec.json | 18 - vripper-ui/src/tslint.json | 17 - vripper-ui/tsconfig.json | 22 - vripper-ui/tslint.json | 130 - vripper-ui/yarn.lock | 9500 ----------------- .../.angulardoc.json | 0 {vripper-ui => vripper-web-ui}/.editorconfig | 0 {vripper-ui => vripper-web-ui}/.gitignore | 0 {vripper-ui => vripper-web-ui}/.prettierrc | 0 {vripper-ui => vripper-web-ui}/README.md | 0 {vripper-ui => vripper-web-ui}/angular.json | 96 +- vripper-web-ui/package.json | 47 + {vripper-ui => vripper-web-ui}/pom.xml | 2 +- .../src/.browserslistrc | 0 .../about-component.component-scss-theme.scss | 11 + .../src/app/about/about-component.html | 0 .../src/app/about/about-component.scss | 0 .../src/app/about/about-component.ts | 12 +- .../src/app/app-routing.module.ts | 2 +- .../src/app/app.component.html | 2 +- .../src/app/app.component.scss | 6 + .../src/app/app.component.scss-theme.scss | 11 + .../src/app/app.component.ts | 10 +- .../src/app/app.module.ts | 6 +- .../confirmation-dialog.html | 0 .../confirmation-dialog.ts | 0 .../src/app/domain/credential.model.ts | 0 .../src/app/domain/download-path.model.ts | 0 .../src/app/domain/event.model.ts | 0 .../src/app/domain/global-state.model.ts | 2 +- .../src/app/domain/multi-post.model.ts | 0 .../src/app/domain/photo-model.ts | 0 .../src/app/domain/post-id.model.ts | 0 .../src/app/domain/post-state.model.ts | 5 +- .../app/domain/remove-all-response.model.ts | 0 .../src/app/domain/remove-response.model.ts | 0 .../src/app/domain/rename-post.model.ts | 0 .../src/app/domain/settings.model.ts | 32 + .../src/app/domain/vr-post-parse.model.ts | 0 .../src/app/domain/ws-message.model.ts | 0 .../app/event-log/event-log.component.html | 4 + .../src/app/event-log/event-log.component.ts | 17 +- .../src/app/event-log/event-log.datasource.ts | 18 +- .../event-log-message-dialog.component.html | 0 .../event-log-message-dialog.component.ts | 0 .../collector-actions-renderer.native.ts | 16 +- .../event-message-renderer.native.ts | 14 +- .../post-added-renderer.native.ts | 14 +- .../post-alt-renderer.native.ts | 14 +- .../post-files-renderer.native.ts | 18 +- .../post-order-renderer.native.ts | 14 +- .../post-progress-renderer.native.ts | 12 +- .../post-status-renderer.native.ts | 12 +- .../progress-renderer.native.ts | 8 +- .../status-renderer.native.ts | 25 +- .../title-renderer.native.ts | 24 +- .../grid-custom-cells/url-renderer.native.ts | 19 +- .../src/app/home/home.component.html | 0 .../src/app/home/home.component.scss | 6 +- .../src/app/home/home.component.ts | 32 +- .../src/app/material.module.ts | 0 .../multi-post-grid-data.source.ts | 20 +- .../multi-post-grid.component.html | 4 + .../multi-post-grid.component.ts | 25 +- .../multi-post-items.component.html | 0 .../multi-post-items.component.scss | 0 .../multi-post-items.component.ts | 54 +- .../src/app/photos/photos.component.html | 2 +- .../src/app/photos/photos.component.scss | 0 .../src/app/photos/photos.component.ts | 21 +- .../src/app/photos/photos.datasource.ts | 15 +- .../post-context-menu.component.html | 12 - .../post-context-menu.component.scss | 0 .../post-context-menu.component.ts | 120 +- .../src/app/posts/posts.component.html | 4 + .../src/app/posts/posts.component.ts | 29 +- .../src/app/posts/posts.datasource.ts | 18 +- .../preview-tooltip.component.ts | 0 .../preview-tooltip.directive.ts | 4 +- .../src/app/scan/scan.component.html | 0 .../src/app/scan/scan.component.scss | 0 .../src/app/scan/scan.component.ts | 6 +- .../src/app/services/app.service.ts | 4 +- .../src/app/services/event-log.service.ts | 4 +- .../src/app/services/home-tabs.service.ts | 0 .../app/services/link-collector.service.ts | 4 +- .../src/app/services/multi-post.service.ts | 0 .../app/services/post-context-menu.service.ts | 4 +- .../src/app/services/posts.service.ts | 10 +- .../src/app/services/selection-service.ts | 8 +- .../src/app/services/server-service.ts | 4 +- .../src/app/services/ws-connection.service.ts | 82 +- .../src/app/settings/settings.component.html | 42 +- .../src/app/settings/settings.component.scss | 0 .../src/app/settings/settings.component.ts | 82 +- .../app/status-bar/status-bar.component.html | 2 +- .../app/status-bar/status-bar.component.scss | 0 .../status-bar.component.scss-theme.scss | 12 + .../app/status-bar/status-bar.component.ts | 5 +- .../src/app/toolbar/toolbar.component.html | 7 +- .../src/app/toolbar/toolbar.component.scss | 0 .../toolbar/toolbar.component.scss-theme.scss | 37 + .../src/app/toolbar/toolbar.component.ts | 56 +- .../src/assets/.gitkeep | 0 .../src/assets/OpenSans-Regular.ttf | Bin .../src/assets/icon.png | Bin .../src/assets/material-icons.woff2 | Bin .../src/assets/paypal.svg | 0 .../src/assets/v.svg | 0 .../src/environments/environment.prod.ts | 2 +- .../src/environments/environment.ts | 6 +- .../src/favicon.ico | Bin vripper-web-ui/src/grid-theme.scss | 150 + {vripper-ui => vripper-web-ui}/src/index.html | 0 {vripper-ui => vripper-web-ui}/src/main.ts | 6 - vripper-web-ui/src/mat-theme.scss | 33 + .../src/styles.scss | 20 +- vripper-web-ui/tsconfig.app.json | 14 + vripper-web-ui/tsconfig.json | 32 + vripper-web-ui/tsconfig.spec.json | 14 + {vripper-ui => vripper-web-ui}/vripper-ui.iml | 0 vripper-web-ui/vripper-web-ui.iml | 12 + vripper-web-ui/yarn.lock | 7299 +++++++++++++ vripper-web/.gitignore | 33 + vripper-web/.mvn/wrapper/maven-wrapper.jar | Bin 0 -> 59925 bytes .../.mvn/wrapper/maven-wrapper.properties | 18 + vripper-web/mvnw | 316 + vripper-web/mvnw.cmd | 188 + vripper-web/pom.xml | 84 + .../me/mnlr/vripper/VripperWebApplication.kt | 11 + .../web/restendpoints/EventLogRestEndpoint.kt | 18 + .../web/restendpoints/PostRestEndpoint.kt | 107 + .../web/restendpoints/SettingsRestEndpoint.kt | 56 + .../vripper/web/restendpoints/WebConfig.kt | 12 + .../web/restendpoints/domain/AltPostName.kt | 3 + .../web/restendpoints/domain/DownloadPath.kt | 3 + .../web/restendpoints/domain/PostId.kt | 3 + .../web/restendpoints/domain/PostToAdd.kt | 3 + .../restendpoints/domain/RemoveAllResult.kt | 3 + .../web/restendpoints/domain/RemoveResult.kt | 3 + .../vripper/web/restendpoints/domain/Theme.kt | 3 + .../web/restendpoints/domain/ThreadId.kt | 3 + .../web/restendpoints/domain/ThreadUrl.kt | 3 + .../exceptions/BadRequestException.java | 2 +- .../exceptions/NotFoundException.java | 2 +- .../exceptions/ServerErrorException.java | 2 +- .../vripper/web/wsendpoints/DataBroadcast.kt | 133 + .../vripper/web/wsendpoints/DataController.kt | 78 + .../web/wsendpoints/WebSocketConfig.kt | 20 + vripper.iml | 12 - 461 files changed, 17125 insertions(+), 20927 deletions(-) create mode 100644 .github/workflows/maven-publish.yml rename {vripper-server => vripper-core}/.gitignore (100%) rename {vripper-server => vripper-core}/.mvn/wrapper/maven-wrapper.jar (100%) rename {vripper-server => vripper-core}/.mvn/wrapper/maven-wrapper.properties (100%) rename {vripper-server => vripper-core}/mvnw (100%) rename {vripper-server => vripper-core}/mvnw.cmd (100%) create mode 100644 vripper-core/pom.xml create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/AppEndpointService.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/EventListenerBean.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/Management.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/SpringContext.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/Utils.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/delegate/LoggerDelegate.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/download/DownloadService.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/download/ImageDownloadContext.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/download/ImageDownloadRunnable.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/download/PostDownloadRunnable.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/entities/ImageDownloadState.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/entities/LogEvent.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/entities/Metadata.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/entities/PostDownloadState.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/entities/Thread.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/entities/domain/Status.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/event/Event.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/event/EventBus.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/exception/DownloadException.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/exception/HostException.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/exception/HtmlProcessorException.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/exception/PostParseException.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/exception/QueueException.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/exception/RenameException.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/exception/ValidationException.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/exception/VripperException.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/exception/XpathException.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/host/AcidimgHost.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/host/DPicMeHost.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/host/Host.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/host/ImageBamHost.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/host/ImageTwistHost.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/host/ImageVenueHost.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/host/ImageZillaHost.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/host/ImgSpiceHost.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/host/ImgboxHost.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/host/ImxHost.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/host/PimpandhostHost.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/host/PixRouteHost.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/host/PixhostHost.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/host/PixxxelsHost.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/host/PostImgHost.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/host/TurboImageHost.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/host/ViprImHost.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/model/DownloadSpeed.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/model/GlobalState.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/model/ImageItem.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/model/LoggedUser.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/model/PostItem.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/model/Settings.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/model/ThreadInput.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/model/ThreadItem.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/parser/ThreadLookupAPIParser.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/parser/ThreadLookupAPIResponseHandler.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/repositories/ImageRepository.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/repositories/LogEventRepository.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/repositories/MetadataRepository.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/repositories/PostDownloadStateRepository.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/repositories/ThreadRepository.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/repositories/impl/ImageRepositoryImpl.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/repositories/impl/LogEventRepositoryImpl.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/repositories/impl/MetadataRepositoryImpl.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/repositories/impl/PostDownloadStateRepositoryImpl.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/repositories/impl/ThreadRepositoryImpl.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/services/DataTransaction.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/services/DownloadSpeedService.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/services/GlobalStateService.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/services/HTTPService.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/services/HtmlProcessorService.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/services/MetadataService.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/services/PathService.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/services/RetryPolicyService.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/services/SettingsService.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/services/ThreadCacheService.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/services/ThreadPoolService.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/services/VGAuthService.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/services/XpathService.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/tasks/LeaveThanksRunnable.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/tasks/LinkScanRunnable.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/tasks/MetadataRunnable.kt create mode 100644 vripper-core/src/main/kotlin/me/mnlr/vripper/tasks/ThreadLookupRunnable.kt rename {vripper-server => vripper-core}/src/main/resources/application.properties (95%) create mode 100644 vripper-core/src/main/resources/db/changelog/db.changelog-master.xml create mode 100644 vripper-core/src/main/resources/logback-spring.xml rename {vripper-server => vripper-core}/src/main/resources/proxies.json (100%) delete mode 100644 vripper-electron/.gitignore delete mode 100644 vripper-electron/.jshintrc delete mode 100644 vripper-electron/build/icon.icns delete mode 100644 vripper-electron/build/icon.ico delete mode 100644 vripper-electron/build/icon.png delete mode 100644 vripper-electron/build/icons/1024x1024.png delete mode 100644 vripper-electron/build/icons/128x128.png delete mode 100644 vripper-electron/build/icons/16x16.png delete mode 100644 vripper-electron/build/icons/256x256.png delete mode 100644 vripper-electron/build/icons/32x32.png delete mode 100644 vripper-electron/build/icons/48x48.png delete mode 100644 vripper-electron/build/icons/512x512.png delete mode 100644 vripper-electron/build/icons/64x64.png delete mode 100644 vripper-electron/main.js delete mode 100644 vripper-electron/package.json delete mode 100644 vripper-electron/pom.xml delete mode 100644 vripper-electron/pre-build.js delete mode 100644 vripper-electron/renderer.js delete mode 100644 vripper-electron/vripper-electron.iml delete mode 100644 vripper-electron/yarn.lock create mode 100644 vripper-gui/.gitignore create mode 100644 vripper-gui/.mvn/wrapper/maven-wrapper.jar create mode 100644 vripper-gui/.mvn/wrapper/maven-wrapper.properties create mode 100644 vripper-gui/mvnw create mode 100644 vripper-gui/mvnw.cmd create mode 100644 vripper-gui/pom.xml create mode 100644 vripper-gui/src/main/kotlin/me/mnlr/vripper/VripperGuiApplication.kt create mode 100644 vripper-gui/src/main/kotlin/me/mnlr/vripper/controller/GlobalStateController.kt create mode 100644 vripper-gui/src/main/kotlin/me/mnlr/vripper/controller/ImageController.kt create mode 100644 vripper-gui/src/main/kotlin/me/mnlr/vripper/controller/LogController.kt create mode 100644 vripper-gui/src/main/kotlin/me/mnlr/vripper/controller/PostController.kt create mode 100644 vripper-gui/src/main/kotlin/me/mnlr/vripper/controller/SettingsController.kt create mode 100644 vripper-gui/src/main/kotlin/me/mnlr/vripper/controller/ThreadController.kt create mode 100644 vripper-gui/src/main/kotlin/me/mnlr/vripper/event/ApplicationInitialized.kt create mode 100644 vripper-gui/src/main/kotlin/me/mnlr/vripper/gui/Styles.kt create mode 100644 vripper-gui/src/main/kotlin/me/mnlr/vripper/model/GlobalStateModel.kt create mode 100644 vripper-gui/src/main/kotlin/me/mnlr/vripper/model/ImageModel.kt create mode 100644 vripper-gui/src/main/kotlin/me/mnlr/vripper/model/LogModel.kt create mode 100644 vripper-gui/src/main/kotlin/me/mnlr/vripper/model/PostModel.kt create mode 100644 vripper-gui/src/main/kotlin/me/mnlr/vripper/model/ThreadModel.kt create mode 100644 vripper-gui/src/main/kotlin/me/mnlr/vripper/model/ThreadSelectionModel.kt create mode 100644 vripper-gui/src/main/kotlin/me/mnlr/vripper/model/settings/ConnectionSettingsModel.kt create mode 100644 vripper-gui/src/main/kotlin/me/mnlr/vripper/model/settings/DownloadSettingsModel.kt create mode 100644 vripper-gui/src/main/kotlin/me/mnlr/vripper/model/settings/ViperSettingsModel.kt create mode 100644 vripper-gui/src/main/kotlin/me/mnlr/vripper/view/AppView.kt create mode 100644 vripper-gui/src/main/kotlin/me/mnlr/vripper/view/LoadingView.kt create mode 100644 vripper-gui/src/main/kotlin/me/mnlr/vripper/view/actionbar/ActionBarView.kt create mode 100644 vripper-gui/src/main/kotlin/me/mnlr/vripper/view/actionbar/AddActionsView.kt create mode 100644 vripper-gui/src/main/kotlin/me/mnlr/vripper/view/actionbar/DownloadActionsView.kt create mode 100644 vripper-gui/src/main/kotlin/me/mnlr/vripper/view/actionbar/LogActionsView.kt create mode 100644 vripper-gui/src/main/kotlin/me/mnlr/vripper/view/actionbar/ThreadActionsView.kt create mode 100644 vripper-gui/src/main/kotlin/me/mnlr/vripper/view/main/MainView.kt create mode 100644 vripper-gui/src/main/kotlin/me/mnlr/vripper/view/posts/AddView.kt create mode 100644 vripper-gui/src/main/kotlin/me/mnlr/vripper/view/settings/ConnectionSettingsView.kt create mode 100644 vripper-gui/src/main/kotlin/me/mnlr/vripper/view/settings/DownloadSettingsView.kt create mode 100644 vripper-gui/src/main/kotlin/me/mnlr/vripper/view/settings/SettingsView.kt create mode 100644 vripper-gui/src/main/kotlin/me/mnlr/vripper/view/settings/ViperSettingsView.kt create mode 100644 vripper-gui/src/main/kotlin/me/mnlr/vripper/view/status/StatusBarView.kt create mode 100644 vripper-gui/src/main/kotlin/me/mnlr/vripper/view/tables/ImagesTableView.kt create mode 100644 vripper-gui/src/main/kotlin/me/mnlr/vripper/view/tables/LogTableView.kt create mode 100644 vripper-gui/src/main/kotlin/me/mnlr/vripper/view/tables/PostsTableView.kt create mode 100644 vripper-gui/src/main/kotlin/me/mnlr/vripper/view/tables/ThreadSelectionTableView.kt create mode 100644 vripper-gui/src/main/kotlin/me/mnlr/vripper/view/tables/ThreadTableView.kt create mode 100644 vripper-gui/src/main/resources/analyze.png create mode 100644 vripper-gui/src/main/resources/broom.png create mode 100644 vripper-gui/src/main/resources/bug.png create mode 100644 vripper-gui/src/main/resources/data-transfer.png create mode 100644 vripper-gui/src/main/resources/details.png create mode 100644 vripper-gui/src/main/resources/download.png create mode 100644 vripper-gui/src/main/resources/downloads-folder.png create mode 100644 vripper-gui/src/main/resources/end.png create mode 100644 vripper-gui/src/main/resources/icons/1024x1024.png create mode 100644 vripper-gui/src/main/resources/icons/128x128.png create mode 100644 vripper-gui/src/main/resources/icons/16x16.png create mode 100644 vripper-gui/src/main/resources/icons/256x256.png create mode 100644 vripper-gui/src/main/resources/icons/32x32.png create mode 100644 vripper-gui/src/main/resources/icons/48x48.png create mode 100644 vripper-gui/src/main/resources/icons/512x512.png create mode 100644 vripper-gui/src/main/resources/icons/64x64.png create mode 100644 vripper-gui/src/main/resources/pause.png create mode 100644 vripper-gui/src/main/resources/play.png create mode 100644 vripper-gui/src/main/resources/plus.png create mode 100644 vripper-gui/src/main/resources/popup.png create mode 100644 vripper-gui/src/main/resources/rename.png create mode 100644 vripper-gui/src/main/resources/save.png create mode 100644 vripper-gui/src/main/resources/search.png create mode 100644 vripper-gui/src/main/resources/settings.png create mode 100644 vripper-gui/src/main/resources/stop.png create mode 100644 vripper-gui/src/main/resources/trash.png delete mode 100644 vripper-server/pom.xml delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/EventListenerBean.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/SpringContext.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/Utils.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/VripperApplication.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/download/DownloadJob.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/download/DownloadJobWrapper.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/download/DownloadService.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/event/Event.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/event/EventBus.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/exception/DownloadException.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/exception/HostException.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/exception/HtmlProcessorException.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/exception/PostParseException.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/exception/QueueException.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/exception/RenameException.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/exception/ValidationException.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/exception/VripperException.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/exception/XpathException.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/host/AcidimgHost.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/host/DPicMeHost.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/host/Host.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/host/ImageBamHost.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/host/ImageTwistHost.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/host/ImageVenueHost.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/host/ImageZillaHost.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/host/ImgSpiceHost.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/host/ImgboxHost.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/host/ImxHost.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/host/PimpandhostHost.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/host/PixRouteHost.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/host/PixhostHost.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/host/PixxxelsHost.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/host/PostImgHost.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/host/TurboImageHost.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/host/ViprImHost.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/jpa/Management.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/jpa/domain/DateTimeSerializer.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/jpa/domain/Image.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/jpa/domain/LogEvent.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/jpa/domain/Metadata.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/jpa/domain/Post.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/jpa/domain/Queued.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/jpa/domain/enums/Status.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/jpa/repositories/IImageRepository.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/jpa/repositories/ILogEventRepository.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/jpa/repositories/IMetadataRepository.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/jpa/repositories/IPostRepository.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/jpa/repositories/IQueuedRepository.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/jpa/repositories/IRepository.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/jpa/repositories/impl/ImageRepository.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/jpa/repositories/impl/LogEventRepository.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/jpa/repositories/impl/MetadataRepository.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/jpa/repositories/impl/PostRepository.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/jpa/repositories/impl/QueuedRepository.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/services/ConnectionService.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/services/DataService.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/services/DownloadSpeedService.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/services/GlobalStateService.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/services/HostService.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/services/HtmlProcessorService.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/services/MetadataService.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/services/PathService.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/services/PostService.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/services/SettingsService.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/services/ThreadPoolService.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/services/VGAuthService.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/services/XpathService.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/services/domain/DownloadSpeed.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/services/domain/GlobalState.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/services/domain/MultiPostItem.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/services/domain/MultiPostScanHandler.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/services/domain/MultiPostScanParser.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/services/domain/MultiPostScanResult.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/services/domain/PostScanHandler.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/services/domain/PostScanParser.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/services/domain/PostScanResult.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/services/domain/Settings.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/tasks/AddPostRunnable.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/tasks/AddQueuedRunnable.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/tasks/LeaveThanksRunnable.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/tasks/LinkScanRunnable.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/tasks/MetadataRunnable.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/web/restendpoints/EventLogRestEndpoint.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/web/restendpoints/PostRestEndpoint.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/web/restendpoints/SettingsRestEndpoint.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/web/restendpoints/WebSecurityConfig.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/web/restendpoints/domain/AltPostName.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/web/restendpoints/domain/DownloadPath.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/web/restendpoints/domain/PostId.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/web/restendpoints/domain/PostToAdd.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/web/restendpoints/domain/RemoveAllResult.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/web/restendpoints/domain/RemoveResult.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/web/restendpoints/domain/ThreadId.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/web/restendpoints/domain/ThreadUrl.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/web/wsendpoints/DataBroadcast.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/web/wsendpoints/DataController.java delete mode 100644 vripper-server/src/main/java/tn/mnlr/vripper/web/wsendpoints/WebSocketConfig.java delete mode 100644 vripper-server/src/main/resources/db/changelog/db.changelog-master.xml delete mode 100644 vripper-server/src/main/resources/logback-spring.xml delete mode 100644 vripper-ui/e2e/protractor.conf.js delete mode 100644 vripper-ui/e2e/src/app.e2e-spec.ts delete mode 100644 vripper-ui/e2e/src/app.po.ts delete mode 100644 vripper-ui/e2e/tsconfig.e2e.json delete mode 100644 vripper-ui/package.json delete mode 100644 vripper-ui/src/app/about/about-component.component-scss-theme.scss delete mode 100644 vripper-ui/src/app/app.component.scss-theme.scss delete mode 100644 vripper-ui/src/app/domain/download-speed.model.ts delete mode 100644 vripper-ui/src/app/domain/logged-user.model.ts delete mode 100644 vripper-ui/src/app/domain/settings.model.ts delete mode 100644 vripper-ui/src/app/event-log/event-log.component.html delete mode 100644 vripper-ui/src/app/multi-post-grid/multi-post-grid.component.html delete mode 100644 vripper-ui/src/app/posts/alternative-title/alternative-title.component.html delete mode 100644 vripper-ui/src/app/posts/alternative-title/alternative-title.component.scss delete mode 100644 vripper-ui/src/app/posts/alternative-title/alternative-title.component.ts delete mode 100644 vripper-ui/src/app/posts/posts.component.html delete mode 100644 vripper-ui/src/app/services/clipboard.service.ts delete mode 100644 vripper-ui/src/app/status-bar/status-bar.component.scss-theme.scss delete mode 100644 vripper-ui/src/app/toolbar/toolbar.component.scss-theme.scss delete mode 100644 vripper-ui/src/dark-theme.scss delete mode 100644 vripper-ui/src/grid-theme.scss delete mode 100644 vripper-ui/src/karma.conf.js delete mode 100644 vripper-ui/src/light-theme.scss delete mode 100644 vripper-ui/src/mat-theme.scss delete mode 100644 vripper-ui/src/polyfills.ts delete mode 100644 vripper-ui/src/test.ts delete mode 100644 vripper-ui/src/tsconfig.app.json delete mode 100644 vripper-ui/src/tsconfig.spec.json delete mode 100644 vripper-ui/src/tslint.json delete mode 100644 vripper-ui/tsconfig.json delete mode 100644 vripper-ui/tslint.json delete mode 100644 vripper-ui/yarn.lock rename {vripper-ui => vripper-web-ui}/.angulardoc.json (100%) rename {vripper-ui => vripper-web-ui}/.editorconfig (100%) rename {vripper-ui => vripper-web-ui}/.gitignore (100%) rename {vripper-ui => vripper-web-ui}/.prettierrc (100%) rename {vripper-ui => vripper-web-ui}/README.md (100%) rename {vripper-ui => vripper-web-ui}/angular.json (53%) create mode 100644 vripper-web-ui/package.json rename {vripper-ui => vripper-web-ui}/pom.xml (99%) rename {vripper-ui => vripper-web-ui}/src/.browserslistrc (100%) create mode 100644 vripper-web-ui/src/app/about/about-component.component-scss-theme.scss rename {vripper-ui => vripper-web-ui}/src/app/about/about-component.html (100%) rename {vripper-ui => vripper-web-ui}/src/app/about/about-component.scss (100%) rename {vripper-ui => vripper-web-ui}/src/app/about/about-component.ts (59%) rename {vripper-ui => vripper-web-ui}/src/app/app-routing.module.ts (85%) rename {vripper-ui => vripper-web-ui}/src/app/app.component.html (86%) rename {vripper-ui => vripper-web-ui}/src/app/app.component.scss (96%) create mode 100644 vripper-web-ui/src/app/app.component.scss-theme.scss rename {vripper-ui => vripper-web-ui}/src/app/app.component.ts (82%) rename {vripper-ui => vripper-web-ui}/src/app/app.module.ts (92%) rename {vripper-ui => vripper-web-ui}/src/app/confirmation-component/confirmation-dialog.html (100%) rename {vripper-ui => vripper-web-ui}/src/app/confirmation-component/confirmation-dialog.ts (100%) rename {vripper-ui => vripper-web-ui}/src/app/domain/credential.model.ts (100%) rename {vripper-ui => vripper-web-ui}/src/app/domain/download-path.model.ts (100%) rename {vripper-ui => vripper-web-ui}/src/app/domain/event.model.ts (100%) rename {vripper-ui => vripper-web-ui}/src/app/domain/global-state.model.ts (54%) rename {vripper-ui => vripper-web-ui}/src/app/domain/multi-post.model.ts (100%) rename {vripper-ui => vripper-web-ui}/src/app/domain/photo-model.ts (100%) rename {vripper-ui => vripper-web-ui}/src/app/domain/post-id.model.ts (100%) rename {vripper-ui => vripper-web-ui}/src/app/domain/post-state.model.ts (77%) rename {vripper-ui => vripper-web-ui}/src/app/domain/remove-all-response.model.ts (100%) rename {vripper-ui => vripper-web-ui}/src/app/domain/remove-response.model.ts (100%) rename {vripper-ui => vripper-web-ui}/src/app/domain/rename-post.model.ts (100%) create mode 100644 vripper-web-ui/src/app/domain/settings.model.ts rename {vripper-ui => vripper-web-ui}/src/app/domain/vr-post-parse.model.ts (100%) rename {vripper-ui => vripper-web-ui}/src/app/domain/ws-message.model.ts (100%) create mode 100644 vripper-web-ui/src/app/event-log/event-log.component.html rename {vripper-ui => vripper-web-ui}/src/app/event-log/event-log.component.ts (87%) rename {vripper-ui => vripper-web-ui}/src/app/event-log/event-log.datasource.ts (62%) rename {vripper-ui => vripper-web-ui}/src/app/event-log/message-dialog/event-log-message-dialog.component.html (100%) rename {vripper-ui => vripper-web-ui}/src/app/event-log/message-dialog/event-log-message-dialog.component.ts (100%) rename {vripper-ui => vripper-web-ui}/src/app/grid-custom-cells/collector-actions-renderer.native.ts (89%) rename {vripper-ui => vripper-web-ui}/src/app/grid-custom-cells/event-message-renderer.native.ts (88%) rename {vripper-ui => vripper-web-ui}/src/app/grid-custom-cells/post-added-renderer.native.ts (81%) rename {vripper-ui => vripper-web-ui}/src/app/grid-custom-cells/post-alt-renderer.native.ts (83%) rename {vripper-ui => vripper-web-ui}/src/app/grid-custom-cells/post-files-renderer.native.ts (83%) rename {vripper-ui => vripper-web-ui}/src/app/grid-custom-cells/post-order-renderer.native.ts (82%) rename {vripper-ui => vripper-web-ui}/src/app/grid-custom-cells/post-progress-renderer.native.ts (75%) rename {vripper-ui => vripper-web-ui}/src/app/grid-custom-cells/post-status-renderer.native.ts (74%) rename {vripper-ui => vripper-web-ui}/src/app/grid-custom-cells/progress-renderer.native.ts (87%) rename {vripper-ui => vripper-web-ui}/src/app/grid-custom-cells/status-renderer.native.ts (91%) rename {vripper-ui => vripper-web-ui}/src/app/grid-custom-cells/title-renderer.native.ts (88%) rename {vripper-ui => vripper-web-ui}/src/app/grid-custom-cells/url-renderer.native.ts (67%) rename {vripper-ui => vripper-web-ui}/src/app/home/home.component.html (100%) rename {vripper-ui => vripper-web-ui}/src/app/home/home.component.scss (89%) rename {vripper-ui => vripper-web-ui}/src/app/home/home.component.ts (57%) rename {vripper-ui => vripper-web-ui}/src/app/material.module.ts (100%) rename {vripper-ui => vripper-web-ui}/src/app/multi-post-grid/multi-post-grid-data.source.ts (63%) create mode 100644 vripper-web-ui/src/app/multi-post-grid/multi-post-grid.component.html rename {vripper-ui => vripper-web-ui}/src/app/multi-post-grid/multi-post-grid.component.ts (75%) rename {vripper-ui => vripper-web-ui}/src/app/multi-post-items/multi-post-items.component.html (100%) rename {vripper-ui => vripper-web-ui}/src/app/multi-post-items/multi-post-items.component.scss (100%) rename {vripper-ui => vripper-web-ui}/src/app/multi-post-items/multi-post-items.component.ts (79%) rename {vripper-ui => vripper-web-ui}/src/app/photos/photos.component.html (86%) rename {vripper-ui => vripper-web-ui}/src/app/photos/photos.component.scss (100%) rename {vripper-ui => vripper-web-ui}/src/app/photos/photos.component.ts (85%) rename {vripper-ui => vripper-web-ui}/src/app/photos/photos.datasource.ts (68%) rename {vripper-ui => vripper-web-ui}/src/app/posts/context-menu/post-context-menu.component.html (70%) rename {vripper-ui => vripper-web-ui}/src/app/posts/context-menu/post-context-menu.component.scss (100%) rename {vripper-ui => vripper-web-ui}/src/app/posts/context-menu/post-context-menu.component.ts (52%) create mode 100644 vripper-web-ui/src/app/posts/posts.component.html rename {vripper-ui => vripper-web-ui}/src/app/posts/posts.component.ts (84%) rename {vripper-ui => vripper-web-ui}/src/app/posts/posts.datasource.ts (63%) rename {vripper-ui => vripper-web-ui}/src/app/preview-tooltip/preview-tooltip.component.ts (100%) rename {vripper-ui => vripper-web-ui}/src/app/preview-tooltip/preview-tooltip.directive.ts (96%) rename {vripper-ui => vripper-web-ui}/src/app/scan/scan.component.html (100%) rename {vripper-ui => vripper-web-ui}/src/app/scan/scan.component.scss (100%) rename {vripper-ui => vripper-web-ui}/src/app/scan/scan.component.ts (92%) rename {vripper-ui => vripper-web-ui}/src/app/services/app.service.ts (97%) rename {vripper-ui => vripper-web-ui}/src/app/services/event-log.service.ts (95%) rename {vripper-ui => vripper-web-ui}/src/app/services/home-tabs.service.ts (100%) rename {vripper-ui => vripper-web-ui}/src/app/services/link-collector.service.ts (92%) rename {vripper-ui => vripper-web-ui}/src/app/services/multi-post.service.ts (100%) rename {vripper-ui => vripper-web-ui}/src/app/services/post-context-menu.service.ts (95%) rename {vripper-ui => vripper-web-ui}/src/app/services/posts.service.ts (80%) rename {vripper-ui => vripper-web-ui}/src/app/services/selection-service.ts (55%) rename {vripper-ui => vripper-web-ui}/src/app/services/server-service.ts (86%) rename {vripper-ui => vripper-web-ui}/src/app/services/ws-connection.service.ts (70%) rename {vripper-ui => vripper-web-ui}/src/app/settings/settings.component.html (79%) rename {vripper-ui => vripper-web-ui}/src/app/settings/settings.component.scss (100%) rename {vripper-ui => vripper-web-ui}/src/app/settings/settings.component.ts (56%) rename {vripper-ui => vripper-web-ui}/src/app/status-bar/status-bar.component.html (82%) rename {vripper-ui => vripper-web-ui}/src/app/status-bar/status-bar.component.scss (100%) create mode 100644 vripper-web-ui/src/app/status-bar/status-bar.component.scss-theme.scss rename {vripper-ui => vripper-web-ui}/src/app/status-bar/status-bar.component.ts (81%) rename {vripper-ui => vripper-web-ui}/src/app/toolbar/toolbar.component.html (90%) rename {vripper-ui => vripper-web-ui}/src/app/toolbar/toolbar.component.scss (100%) create mode 100644 vripper-web-ui/src/app/toolbar/toolbar.component.scss-theme.scss rename {vripper-ui => vripper-web-ui}/src/app/toolbar/toolbar.component.ts (81%) rename {vripper-ui => vripper-web-ui}/src/assets/.gitkeep (100%) rename {vripper-ui => vripper-web-ui}/src/assets/OpenSans-Regular.ttf (100%) rename {vripper-ui => vripper-web-ui}/src/assets/icon.png (100%) rename {vripper-ui => vripper-web-ui}/src/assets/material-icons.woff2 (100%) rename {vripper-ui => vripper-web-ui}/src/assets/paypal.svg (100%) rename {vripper-ui => vripper-web-ui}/src/assets/v.svg (100%) rename {vripper-ui => vripper-web-ui}/src/environments/environment.prod.ts (91%) rename {vripper-ui => vripper-web-ui}/src/environments/environment.ts (88%) rename {vripper-ui => vripper-web-ui}/src/favicon.ico (100%) create mode 100644 vripper-web-ui/src/grid-theme.scss rename {vripper-ui => vripper-web-ui}/src/index.html (100%) rename {vripper-ui => vripper-web-ui}/src/main.ts (57%) create mode 100644 vripper-web-ui/src/mat-theme.scss rename {vripper-ui => vripper-web-ui}/src/styles.scss (84%) create mode 100644 vripper-web-ui/tsconfig.app.json create mode 100644 vripper-web-ui/tsconfig.json create mode 100644 vripper-web-ui/tsconfig.spec.json rename {vripper-ui => vripper-web-ui}/vripper-ui.iml (100%) create mode 100644 vripper-web-ui/vripper-web-ui.iml create mode 100644 vripper-web-ui/yarn.lock create mode 100644 vripper-web/.gitignore create mode 100644 vripper-web/.mvn/wrapper/maven-wrapper.jar create mode 100644 vripper-web/.mvn/wrapper/maven-wrapper.properties create mode 100644 vripper-web/mvnw create mode 100644 vripper-web/mvnw.cmd create mode 100644 vripper-web/pom.xml create mode 100644 vripper-web/src/main/kotlin/me/mnlr/vripper/VripperWebApplication.kt create mode 100644 vripper-web/src/main/kotlin/me/mnlr/vripper/web/restendpoints/EventLogRestEndpoint.kt create mode 100644 vripper-web/src/main/kotlin/me/mnlr/vripper/web/restendpoints/PostRestEndpoint.kt create mode 100644 vripper-web/src/main/kotlin/me/mnlr/vripper/web/restendpoints/SettingsRestEndpoint.kt create mode 100644 vripper-web/src/main/kotlin/me/mnlr/vripper/web/restendpoints/WebConfig.kt create mode 100644 vripper-web/src/main/kotlin/me/mnlr/vripper/web/restendpoints/domain/AltPostName.kt create mode 100644 vripper-web/src/main/kotlin/me/mnlr/vripper/web/restendpoints/domain/DownloadPath.kt create mode 100644 vripper-web/src/main/kotlin/me/mnlr/vripper/web/restendpoints/domain/PostId.kt create mode 100644 vripper-web/src/main/kotlin/me/mnlr/vripper/web/restendpoints/domain/PostToAdd.kt create mode 100644 vripper-web/src/main/kotlin/me/mnlr/vripper/web/restendpoints/domain/RemoveAllResult.kt create mode 100644 vripper-web/src/main/kotlin/me/mnlr/vripper/web/restendpoints/domain/RemoveResult.kt create mode 100644 vripper-web/src/main/kotlin/me/mnlr/vripper/web/restendpoints/domain/Theme.kt create mode 100644 vripper-web/src/main/kotlin/me/mnlr/vripper/web/restendpoints/domain/ThreadId.kt create mode 100644 vripper-web/src/main/kotlin/me/mnlr/vripper/web/restendpoints/domain/ThreadUrl.kt rename {vripper-server/src/main/java/tn => vripper-web/src/main/kotlin/me}/mnlr/vripper/web/restendpoints/exceptions/BadRequestException.java (84%) rename {vripper-server/src/main/java/tn => vripper-web/src/main/kotlin/me}/mnlr/vripper/web/restendpoints/exceptions/NotFoundException.java (83%) rename {vripper-server/src/main/java/tn => vripper-web/src/main/kotlin/me}/mnlr/vripper/web/restendpoints/exceptions/ServerErrorException.java (84%) create mode 100644 vripper-web/src/main/kotlin/me/mnlr/vripper/web/wsendpoints/DataBroadcast.kt create mode 100644 vripper-web/src/main/kotlin/me/mnlr/vripper/web/wsendpoints/DataController.kt create mode 100644 vripper-web/src/main/kotlin/me/mnlr/vripper/web/wsendpoints/WebSocketConfig.kt delete mode 100644 vripper.iml diff --git a/.github/workflows/maven-publish.yml b/.github/workflows/maven-publish.yml new file mode 100644 index 00000000..84f19cef --- /dev/null +++ b/.github/workflows/maven-publish.yml @@ -0,0 +1,34 @@ +# This workflow will build a package using Maven and then publish it to GitHub packages when a release is created +# For more information see: https://github.com/actions/setup-java/blob/main/docs/advanced-usage.md#apache-maven-with-a-settings-path + +name: Maven Package + +on: + release: + types: [created] + +jobs: + build: + + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - uses: actions/checkout@v3 + - name: Set up JDK 17 + uses: actions/setup-java@v3 + with: + java-version: '17' + distribution: 'temurin' + server-id: github # Value of the distributionManagement/repository/id field of the pom.xml + settings-path: ${{ github.workspace }} # location for the settings.xml file + + - name: Build with Maven + run: mvn -B package --file pom.xml + + - name: Publish to GitHub Packages Apache Maven + run: mvn deploy -B -s $GITHUB_WORKSPACE/settings.xml + env: + GITHUB_TOKEN: ${{ github.token }} diff --git a/CHANGELOG.md b/CHANGELOG.md index a34cd87f..35936384 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ # Changelog +## [4.0.0] - 2023-05-03 + +- Drop electron app +- Use javafx for the desktop app +- Re-written in kotlin +- Several enhancements + +### Changed + ## [3.5.4] - 2021-05-29 ### Changed diff --git a/README.md b/README.md index b19e39e7..753bc4de 100644 --- a/README.md +++ b/README.md @@ -1,35 +1,19 @@ # VRipper! -This is my spin for a cross platform gallery ripper app for [vipergirls](https://vipergirls.to) website. The purpose of -this project is to build a robust and clean application to conveniently download photo galleries using modern web -technologies, Java + Spring boot for the back end and angular + electron for the front end. +This is my spin for a cross-platform gallery ripper for [vipergirls.to](https://vipergirls.to). ## How to build -You need a recent version of maven 3.6.1+. +You need JDK 17 and a recent version of maven 3.6.1+ -The application support 2 build modes: +To build run the following: -- Desktop app: Self contained app, built with electron includes Java runtimes. -- Server app: Depends on Java runtimes, you need a web browser to access the app UI. + mvn clean install -Most people will be interested only on the desktop app, however if you have a nas or a server, the server app is there -for you. +Build artifact is located under -To build the server app: + vripper-project\vripper-gui\target\vripper-gui-4.0.0.jar - mvn clean install -DskipTests +Copy the artifact into any other folder and run: -To build the desktop app: - - mvn clean install -Pelectron -DskipTests - -Maven will automatically handle front end compilation. However, for development, you will need to install a recent -version of nodejs on your system. - -If you feel generous and want to support me, you can donate some coins, and I would be very grateful 🙏 - -I accept [PayPal](https://www.paypal.com/myaccount/transfer/homepage/buy/preview) donations, following is my email -address - - dev.vripper@gmail.com + java -jar vripper-gui-4.0.0.jar diff --git a/pom.xml b/pom.xml index 55f1b112..e6b1c7c9 100644 --- a/pom.xml +++ b/pom.xml @@ -1,20 +1,17 @@ - - 4.0.0 - tn.mnlr - vripper - 3.5.4 - pom - - org.springframework.boot - spring-boot-starter-parent - 2.4.5 - - - - vripper-server - vripper-ui - vripper-electron - + + 4.0.0 + me.mnlr + vripper-project + + vripper-core + vripper-gui + + pom + 4.0.0 + + true + diff --git a/vripper-server/.gitignore b/vripper-core/.gitignore similarity index 100% rename from vripper-server/.gitignore rename to vripper-core/.gitignore diff --git a/vripper-server/.mvn/wrapper/maven-wrapper.jar b/vripper-core/.mvn/wrapper/maven-wrapper.jar similarity index 100% rename from vripper-server/.mvn/wrapper/maven-wrapper.jar rename to vripper-core/.mvn/wrapper/maven-wrapper.jar diff --git a/vripper-server/.mvn/wrapper/maven-wrapper.properties b/vripper-core/.mvn/wrapper/maven-wrapper.properties similarity index 100% rename from vripper-server/.mvn/wrapper/maven-wrapper.properties rename to vripper-core/.mvn/wrapper/maven-wrapper.properties diff --git a/vripper-server/mvnw b/vripper-core/mvnw similarity index 100% rename from vripper-server/mvnw rename to vripper-core/mvnw diff --git a/vripper-server/mvnw.cmd b/vripper-core/mvnw.cmd similarity index 100% rename from vripper-server/mvnw.cmd rename to vripper-core/mvnw.cmd diff --git a/vripper-core/pom.xml b/vripper-core/pom.xml new file mode 100644 index 00000000..70c4231b --- /dev/null +++ b/vripper-core/pom.xml @@ -0,0 +1,128 @@ + + + 4.0.0 + + spring-boot-starter-parent + org.springframework.boot + + 3.0.6 + + me.mnlr.vripper + vripper-core + 4.0.0 + vripper-core + vripper-core + + 17 + 1.8.21 + false + + + + + kotlin-reflect + org.jetbrains.kotlin + + + kotlin-stdlib + org.jetbrains.kotlin + + + jackson-module-kotlin + com.fasterxml.jackson.module + + + spring-boot-starter-jdbc + org.springframework.boot + + + liquibase-core + org.liquibase + + + hsqldb + org.hsqldb + runtime + + + httpclient + org.apache.httpcomponents + + + htmlcleaner + net.sourceforge.htmlcleaner + 2.26 + + + reactor-core + io.projectreactor + + + failsafe + net.jodah + 2.4.4 + + + caffeine + com.github.ben-manes.caffeine + + + jackson-databind + com.fasterxml.jackson.core + + + com.fasterxml.jackson.dataformat + jackson-dataformat-yaml + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + + + spring-boot-starter-test + org.springframework.boot + test + + + spring-restdocs-mockmvc + org.springframework.restdocs + test + + + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/test/kotlin + + + org.jetbrains.kotlin + kotlin-maven-plugin + + + -Xjsr305=strict + + + spring + + + + + kotlin-maven-allopen + org.jetbrains.kotlin + ${kotlin.version} + + + + + + + + github + GitHub death-claw Apache Maven Packages + https://maven.pkg.github.com/death-claw/vripper-project + + + diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/AppEndpointService.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/AppEndpointService.kt new file mode 100644 index 00000000..6d6cb394 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/AppEndpointService.kt @@ -0,0 +1,111 @@ +package me.mnlr.vripper + +import org.springframework.stereotype.Service +import me.mnlr.vripper.delegate.LoggerDelegate +import me.mnlr.vripper.download.DownloadService +import me.mnlr.vripper.exception.PostParseException +import me.mnlr.vripper.model.PostItem +import me.mnlr.vripper.repositories.LogEventRepository +import me.mnlr.vripper.repositories.ThreadRepository +import me.mnlr.vripper.services.* +import me.mnlr.vripper.tasks.LinkScanRunnable +import java.util.* +import java.util.regex.Pattern + +@Service +class AppEndpointService( + private val downloadService: DownloadService, + private val dataTransaction: DataTransaction, + private val threadRepository: ThreadRepository, + private val threadCacheService: ThreadCacheService, + private val eventRepository: LogEventRepository, + private val threadPoolService: ThreadPoolService, +) { + private val log by LoggerDelegate() + + @Synchronized + fun scanLinks(postLinks: String) { + if (postLinks.isBlank()) { + log.warn("Nothing to scan") + return + } + val urlList = postLinks.split(Pattern.compile("\\r?\\n")).dropLastWhile { it.isEmpty() }.map { it.trim() } + .filter { it.isNotEmpty() } + threadPoolService.generalExecutor.submit(LinkScanRunnable(urlList)) + } + + @Synchronized + fun restartAll(posIds: List = listOf()) { + downloadService.restartAll(posIds) + } + + @Synchronized + fun download(posts: List>) { + downloadService.download(posts) + } + + @Synchronized + fun stopAll(postIdList: List?) { + downloadService.stopAll(postIdList) + } + + @Synchronized + fun remove(postIdList: List) { + downloadService.stopAll(postIdList) + dataTransaction.removeAll(postIdList) + } + + @Synchronized + fun clearCompleted(): List { + return dataTransaction.clearCompleted() + } + + @Synchronized + @Throws(PostParseException::class) + fun grab(threadId: String): List { + return try { + val thread = threadRepository.findByThreadId(threadId).orElseThrow { + PostParseException( + String.format( + "Unable to find links for threadId = %s", threadId + ) + ) + } + val threadLookupResult = threadCacheService[thread.threadId] + threadLookupResult.postItemList.ifEmpty { + log.error( + String.format( + "Failed to get links for threadId = %s", threadId + ) + ) + throw PostParseException( + String.format( + "Failed to get links for threadId = %s", threadId + ) + ) + } + } catch (e: Exception) { + throw PostParseException( + String.format( + "Failed to get links for threadId = %s, %s", threadId, e.message + ) + ) + } + } + + @Synchronized + fun threadRemove(threadIdList: List) { + threadIdList.forEach { + dataTransaction.removeThread(it) + } + } + + @Synchronized + fun threadClear() { + dataTransaction.clearQueueLinks() + } + + fun logClear() { + eventRepository.deleteAll() + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/EventListenerBean.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/EventListenerBean.kt new file mode 100644 index 00000000..05ec0572 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/EventListenerBean.kt @@ -0,0 +1,19 @@ +package me.mnlr.vripper + +import org.springframework.context.event.ContextRefreshedEvent +import org.springframework.context.event.EventListener +import org.springframework.stereotype.Component +import me.mnlr.vripper.repositories.PostDownloadStateRepository +import me.mnlr.vripper.services.DataTransaction + +@Component +class EventListenerBean( + private val postDownloadStateRepository: PostDownloadStateRepository, + private val dataTransaction: DataTransaction +) { + @EventListener + fun onApplicationEvent(event: ContextRefreshedEvent?) { + postDownloadStateRepository.setDownloadingToStopped() + dataTransaction.sortPostsByRank() + } +} diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/Management.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/Management.kt new file mode 100644 index 00000000..d9720ad9 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/Management.kt @@ -0,0 +1,58 @@ +package me.mnlr.vripper + +import jakarta.annotation.PreDestroy +import org.springframework.beans.factory.annotation.Value +import org.springframework.jdbc.core.JdbcTemplate +import org.springframework.stereotype.Component +import me.mnlr.vripper.download.DownloadService +import me.mnlr.vripper.services.ThreadPoolService +import java.nio.file.Path +import java.time.format.DateTimeFormatter +import java.util.* +import java.util.regex.Pattern + +@Component +class Management( + private val jdbcTemplate: JdbcTemplate, + @Value("\${base.dir}") baseDir: String, + @Value("\${base.dir.name}") baseDirName: String, + private val threadPoolService: ThreadPoolService, + private val downloadService: DownloadService +) { + private val backupFolder: Path + + init { + backupFolder = Path.of(baseDir, baseDirName, "backup") + } + + @PreDestroy + fun destroy() { + threadPoolService.destroy() + downloadService.destroy() + jdbcTemplate.execute("SHUTDOWN") + } + +// @PostConstruct +// @Scheduled(cron = "0 0 0 ? * *") +// private fun backup() { +// +// for (file in Files.list(backupFolder)) { +// val matcher = BACKUP_FILE_PATTERN.matcher(file.fileName.toString()) +// if (matcher.find()) { +// val localDate = LocalDate.parse(matcher.group(1), FORMATTER) +// if (localDate.isEqual(LocalDate.now())) { +// return +// } +// } +// } +// +// // create a backup file +// val backupFile = backupFolder.resolve("db_${LocalDateTime.now().format(FORMATTER)}.tar.gz") +// jdbcTemplate.execute("BACKUP DATABASE TO '${backupFile}' BLOCKING") +// } + + companion object { + val FORMATTER: DateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd") + private val BACKUP_FILE_PATTERN = Pattern.compile("^db_(\\d{4}-\\d{2}-\\d{2})\\.tar\\.gz$") + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/SpringContext.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/SpringContext.kt new file mode 100644 index 00000000..28b51890 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/SpringContext.kt @@ -0,0 +1,37 @@ +package me.mnlr.vripper + +import org.springframework.beans.BeansException +import org.springframework.context.ApplicationContext +import org.springframework.context.ApplicationContextAware +import org.springframework.context.ConfigurableApplicationContext +import org.springframework.scheduling.annotation.EnableScheduling +import org.springframework.stereotype.Component +import me.mnlr.vripper.delegate.LoggerDelegate + +@Component +@EnableScheduling +class SpringContext : ApplicationContextAware { + @Throws(BeansException::class) + override fun setApplicationContext(context: ApplicationContext) { + + // store ApplicationContext reference to access required beans later on + Companion.context = context as ConfigurableApplicationContext + } + + companion object { + private val log by LoggerDelegate() + private lateinit var context: ConfigurableApplicationContext + fun getBean(beanClass: Class): T { + return context.getBean(beanClass) + } + + fun getBeansOfType(beanClass: Class): Map { + return context.getBeansOfType(beanClass) + } + + fun close() { + log.info("Application terminating...") + context.close() + } + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/Utils.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/Utils.kt new file mode 100644 index 00000000..1c98dca9 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/Utils.kt @@ -0,0 +1,39 @@ +package me.mnlr.vripper + +import java.io.PrintWriter +import java.io.StringWriter + +fun Throwable.formatToString(): String { + return StringWriter().use { stringWriter -> + PrintWriter(stringWriter).use { printWriter -> + this.printStackTrace(printWriter) + printWriter.flush() + stringWriter.flush() + stringWriter.toString() + } + } +} + +fun Long.formatSI(): String { + return humanReadableByteCount(this, false) +} + +private fun humanReadableByteCount(bytes: Long, si: Boolean): String { + val unit = if (si) 1000 else 1024 + if (bytes < unit) return "$bytes B" + val exp = (Math.log(bytes.toDouble()) / Math.log(unit.toDouble())).toInt() + val pre = (if (si) "kMGTPE" else "KMGTPE")[exp - 1].toString() + if (si) "" else "i" + return String.format("%.1f %sB", bytes / Math.pow(unit.toDouble(), exp.toDouble()), pre) +} + + +fun getExtension(fileName: String): String { + return if (fileName.contains(".")) fileName.substring(fileName.lastIndexOf(".") + 1) else "" +} + +fun getFileNameWithoutExtension(fileName: String): String { + return if (fileName.contains(".")) fileName.substring( + 0, + fileName.lastIndexOf(".") + ) else fileName +} diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/delegate/LoggerDelegate.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/delegate/LoggerDelegate.kt new file mode 100644 index 00000000..22a8df6d --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/delegate/LoggerDelegate.kt @@ -0,0 +1,16 @@ +package me.mnlr.vripper.delegate + +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import kotlin.properties.ReadOnlyProperty +import kotlin.reflect.KProperty + +class LoggerDelegate : ReadOnlyProperty { + private var logger: Logger? = null + override operator fun getValue(thisRef: Any?, property: KProperty<*>): Logger { + if (logger == null) { + logger = LoggerFactory.getLogger(thisRef!!.javaClass) + } + return logger!! + } +} diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/download/DownloadService.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/download/DownloadService.kt new file mode 100644 index 00000000..e0274154 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/download/DownloadService.kt @@ -0,0 +1,297 @@ +package me.mnlr.vripper.download + +import jakarta.annotation.PostConstruct +import net.jodah.failsafe.Failsafe +import net.jodah.failsafe.RetryPolicy +import org.springframework.beans.factory.annotation.Value +import org.springframework.stereotype.Service +import me.mnlr.vripper.delegate.LoggerDelegate +import me.mnlr.vripper.entities.ImageDownloadState +import me.mnlr.vripper.entities.LogEvent +import me.mnlr.vripper.entities.PostDownloadState +import me.mnlr.vripper.entities.domain.Status +import me.mnlr.vripper.formatToString +import me.mnlr.vripper.host.Host +import me.mnlr.vripper.repositories.ImageRepository +import me.mnlr.vripper.repositories.LogEventRepository +import me.mnlr.vripper.repositories.PostDownloadStateRepository +import me.mnlr.vripper.services.* +import java.util.* +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors +import java.util.concurrent.TimeUnit +import java.util.concurrent.locks.ReentrantLock +import java.util.stream.Collectors +import kotlin.concurrent.withLock + +@Service +class DownloadService( + @param:Value("\${download.pool-size}") private val maxPoolSize: Int, + private val settingsService: SettingsService, + private val dataTransaction: DataTransaction, + private val metadataService: MetadataService, + private val hosts: List, + private val eventRepository: LogEventRepository, + private val retryPolicyService: RetryPolicyService, + private val postDownloadStateRepository: PostDownloadStateRepository, + private val imageRepository: ImageRepository, + private val threadPoolService: ThreadPoolService, +) { + private val log by LoggerDelegate() + + // Class fields + private val executor: ExecutorService = Executors.newFixedThreadPool(maxPoolSize) + private val running: MutableMap> = mutableMapOf() + private val pending: MutableMap> = mutableMapOf() + private val lock = ReentrantLock() + private val condition = lock.newCondition() + private val pollThread: Thread + + init { + pollThread = Thread( + { + val accepted: MutableList = mutableListOf() + val candidates: MutableList = mutableListOf() + while (!Thread.interrupted()) { + lock.withLock { + candidates.addAll(getCandidates(candidateCount())) + candidates.forEach { + if (canRun(it.context.image.host)) { + accepted.add(it) + running[it.context.image.host]!!.add(it) + log.debug("${it.context.image.url} accepted to run") + } + } + accepted.forEach { + pending[it.context.image.host]?.remove(it) + schedule(it) + } + accepted.clear() + candidates.clear() + condition.await() + } + } + }, "Download scheduler thread" + ) + } + + @PostConstruct + fun init() { + pollThread.start() + } + + fun destroy() { + log.info("Shutting down ExecutionService") + pollThread.interrupt() + executor.shutdown() + stop(postDownloadStateRepository.findAll().map { it.postId }) + if (!executor.awaitTermination(5, TimeUnit.SECONDS)) { + log.warn("Some jobs are still running!, forcing shutdown") + executor.shutdownNow() + } + } + + fun stopAll(postIds: List?) { + if (postIds != null) { + stop(postIds) + } else { + stop(postDownloadStateRepository.findAll().map(PostDownloadState::postId)) + } + } + + fun restartAll(postIds: List = listOf()) { + if (postIds.isNotEmpty()) { + restart(postIds) + } else { + restart(postDownloadStateRepository.findAll().map(PostDownloadState::postId)) + } + } + + private fun restart(postIds: List) { + lock.withLock { + val data: MutableMap> = mutableMapOf() + for (postId in postIds) { + if (isPending(postId)) { + log.warn("Cannot restart, jobs are currently running for post id $postIds") + continue + } + val imageDownloadStates: List = + imageRepository.findByPostIdAndIsNotCompleted(postId) + if (imageDownloadStates.isEmpty()) { + continue + } + val postDownloadState: PostDownloadState = + postDownloadStateRepository.findByPostId(postId).orElseThrow() + log.debug("Restarting ${imageDownloadStates.size} jobs for post id $postIds") + postDownloadState.status = Status.PENDING + dataTransaction.update(postDownloadState) + data[postDownloadState] = imageDownloadStates + } + + for ((postDownloadState, imageDownloadStates) in data) { + postDownloadState.status = Status.PENDING + dataTransaction.update(postDownloadState) + for (imageDownloadState in imageDownloadStates) { + log.debug("Enqueuing a job for ${imageDownloadState.url}") + with(imageDownloadState) { + this.status = Status.STOPPED + this.current = 0 + } + dataTransaction.update(imageDownloadState) + val imageDownloadRunnable = ImageDownloadRunnable( + imageDownloadState.id!!, settingsService.settings + ) + pending.computeIfAbsent( + imageDownloadState.host + ) { mutableListOf() } + pending[imageDownloadState.host]!!.add(imageDownloadRunnable) + } + } + condition.signal() + } + } + + private fun isPending(postId: String): Boolean { + lock.withLock { + return pending.values.flatten().any { it.context.image.postId == postId } + } + } + + private fun isRunning(postId: String): Boolean { + lock.withLock { + return running.values.flatten().any { it.context.image.postId == postId } + } + } + + private fun stop(postIds: List) { + lock.withLock { + for (postId in postIds) { + val postDownloadState: PostDownloadState = + postDownloadStateRepository.findByPostId(postId).orElseThrow() + pending.values.forEach { pending -> + pending.removeIf { it.context.image.postId == postId } + } + running.values.flatten() + .filter { p: ImageDownloadRunnable -> p.context.image.postId == postId } + .forEach { obj: ImageDownloadRunnable -> obj.stop() } + dataTransaction.stopImagesByPostIdAndIsNotCompleted(postId) + dataTransaction.finishPost(postDownloadState) + } + metadataService.stopFetchingMetadata(postIds) + } + } + + private fun canRun(host: Host): Boolean { + val totalRunning = running.values.sumOf { it.size } + return (running[host]!!.size < settingsService.settings.connectionSettings.maxThreads && if (settingsService.settings.connectionSettings.maxTotalThreads == 0) totalRunning < maxPoolSize else totalRunning < settingsService.settings.connectionSettings.maxTotalThreads) + } + + private fun candidateCount(): Map { + val map: MutableMap = mutableMapOf() + hosts.forEach { h: Host -> + val imageDownloadRunnableList: List = running.computeIfAbsent( + h + ) { mutableListOf() } + val count: Int = settingsService.settings.connectionSettings.maxThreads - imageDownloadRunnableList.size + log.debug("Download slots for ${h.host}: $count") + map[h] = count + } + return map + } + + private fun getCandidates(candidateCount: Map): List { + val hostIntegerMap: MutableMap = candidateCount.toMutableMap() + val candidates: MutableList = mutableListOf() + hosts@ for (host in pending.keys) { + val list: List = + pending[host]!!.sortedWith(Comparator.comparingInt { it.context.post.rank } + .thenComparingInt { it.context.image.index }) + for (imageDownloadRunnable in list) { + val count = hostIntegerMap[host] ?: 0 + if (count > 0) { + candidates.add(imageDownloadRunnable) + hostIntegerMap[host] = count - 1 + } else { + continue@hosts + } + } + } + if (log.isDebugEnabled) { + val collect: Map> = + candidates.stream().collect(Collectors.groupingBy { it.context.image.host }) + collect.forEach { + log.debug( + "Candidate download for ${it.key.host} ${it.value.size}/${candidateCount[it.key]}" + ) + } + } + return candidates.sortedWith(Comparator.comparing { v: ImageDownloadRunnable -> v.context.post.rank }) + } + + private fun schedule(imageDownloadRunnable: ImageDownloadRunnable) { + log.debug("Scheduling a job for ${imageDownloadRunnable.context.image.url}") + executor.execute { + try { + Failsafe.with>(retryPolicyService.buildRetryPolicyForDownload()) + .onFailure { + try { + eventRepository.save( + LogEvent( + type = LogEvent.Type.DOWNLOAD, + status = LogEvent.Status.ERROR, + message = "Failed to download ${imageDownloadRunnable.context.image.url}\n ${it.failure.formatToString()}" + ) + ) + } catch (exp: Exception) { + log.error("Failed to save event", exp) + } + log.error( + "Failed to download ${imageDownloadRunnable.context.image.url} after ${it.attemptCount} tries", + it.failure + ) + val image = imageDownloadRunnable.context.image + image.status = Status.ERROR + dataTransaction.update(image) + }.onComplete { + + afterJobFinish(imageDownloadRunnable) + log.debug( + "Finished downloading ${imageDownloadRunnable.context.image.url}" + ) + }.run(imageDownloadRunnable::run) + } catch (ignored: Exception) { + } + } + } + + fun afterJobFinish(imageDownloadRunnable: ImageDownloadRunnable) { + lock.withLock { + running[imageDownloadRunnable.context.image.host]!!.remove(imageDownloadRunnable) + if (!isPending(imageDownloadRunnable.context.image.postId) && !isRunning( + imageDownloadRunnable.context.image.postId + ) + ) { + dataTransaction.finishPost(imageDownloadRunnable.context.post) + } + condition.signal() + } + } + + fun pendingCount(): Int { + return pending.values.sumOf { it.size } + } + + fun runningCount(): Int { + return running.values.sumOf { it.size } + } + + fun download(posts: List>) { + for (post in posts) { + threadPoolService.generalExecutor.submit( + PostDownloadRunnable( + post.first, post.second + ) + ) + } + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/download/ImageDownloadContext.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/download/ImageDownloadContext.kt new file mode 100644 index 00000000..38a210bf --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/download/ImageDownloadContext.kt @@ -0,0 +1,25 @@ +package me.mnlr.vripper.download + +import org.apache.http.client.protocol.HttpClientContext +import me.mnlr.vripper.SpringContext +import me.mnlr.vripper.entities.ImageDownloadState +import me.mnlr.vripper.entities.PostDownloadState +import me.mnlr.vripper.repositories.ImageRepository +import me.mnlr.vripper.repositories.PostDownloadStateRepository + +class ImageDownloadContext(val imageId: Long) { + + private val imageRepository: ImageRepository = + SpringContext.getBean(ImageRepository::class.java) + private val postDownloadStateRepository: PostDownloadStateRepository = + SpringContext.getBean(PostDownloadStateRepository::class.java) + + + val httpContext: HttpClientContext = HttpClientContext.create() + val postId = image.postIdRef + var stopped = false + val image: ImageDownloadState + get() = imageRepository.findById(imageId).orElseThrow() + val post: PostDownloadState + get() = postDownloadStateRepository.findById(postId).orElseThrow() +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/download/ImageDownloadRunnable.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/download/ImageDownloadRunnable.kt new file mode 100644 index 00000000..9dab397a --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/download/ImageDownloadRunnable.kt @@ -0,0 +1,178 @@ +package me.mnlr.vripper.download + +import net.jodah.failsafe.function.CheckedRunnable +import org.apache.http.client.protocol.HttpClientContext +import org.apache.http.impl.client.BasicCookieStore +import me.mnlr.vripper.SpringContext +import me.mnlr.vripper.delegate.LoggerDelegate +import me.mnlr.vripper.entities.ImageDownloadState +import me.mnlr.vripper.entities.PostDownloadState +import me.mnlr.vripper.entities.domain.Status +import me.mnlr.vripper.exception.DownloadException +import me.mnlr.vripper.exception.HostException +import me.mnlr.vripper.getExtension +import me.mnlr.vripper.host.DownloadedImage +import me.mnlr.vripper.host.ImageMimeType +import me.mnlr.vripper.model.Settings +import me.mnlr.vripper.services.* +import java.io.IOException +import java.nio.file.Files +import java.nio.file.Path +import java.nio.file.StandardCopyOption +import java.util.* +import kotlin.io.path.pathString + +class ImageDownloadRunnable( + private val imageInternalId: Long, private val settings: Settings +) : CheckedRunnable { + private val log by LoggerDelegate() + + private val dataTransaction: DataTransaction = SpringContext.getBean(DataTransaction::class.java) + private val pathService: PathService = SpringContext.getBean(PathService::class.java) + + val context: ImageDownloadContext = ImageDownloadContext(imageInternalId) + private val image: ImageDownloadState + get() = context.image + private var stopped: Boolean + get() = context.stopped + set(value) { + context.stopped = value + } + private val httpContext: HttpClientContext + get() = context.httpContext + + init { + httpContext.cookieStore = BasicCookieStore() + httpContext.setAttribute( + HTTPService.ContextAttributes.CONTEXT_ATTRIBUTES, HTTPService.ContextAttributes() + ) + } + + @Throws(DownloadException::class) + fun download() { + try { + val image = image + with(image) { + this.status = Status.DOWNLOADING + this.current = 0 + } + dataTransaction.update(image) + synchronized(image.postId.intern()) { + val post = context.post + if (post.status != Status.DOWNLOADING) { + post.status = Status.DOWNLOADING + dataTransaction.update(post) + } + } + + if (stopped) { + return + } + log.debug("Getting image url and name from ${image.url} using ${image.host}") + val downloadedImage = image.host.downloadInternal(image.url, context) + log.debug("Resolved name for ${image.url}: ${downloadedImage.name}") + log.debug( + "Downloaded image ${image.url} to ${downloadedImage.path}" + ) + val sanitizedFileName = pathService.sanitize(downloadedImage.name) + log.debug( + "Sanitizing image name from ${downloadedImage.name} to $sanitizedFileName" + ) + checkImageTypeAndRename( + context.post, downloadedImage, image.index + ) + synchronized(image.postId.intern()) { + val post = context.post + post.done += 1 + dataTransaction.update(post) + } + } catch (e: Exception) { + if (stopped) { + return + } + throw DownloadException(e) + } finally { + val image = image + if (image.current == image.total && image.total > 0) { + image.status = Status.FINISHED + } else if (stopped) { + image.status = Status.STOPPED + } else { + image.status = Status.ERROR + } + dataTransaction.update(image) + } + } + + @Throws(HostException::class) + private fun checkImageTypeAndRename( + postDownloadState: PostDownloadState, downloadedImage: DownloadedImage, index: Int + ) { + val existingExtension = getExtension(downloadedImage.name) + val extension = when (downloadedImage.type) { + ImageMimeType.IMAGE_BMP -> "BMP" + ImageMimeType.IMAGE_GIF -> "GIF" + ImageMimeType.IMAGE_JPEG -> "JPG" + ImageMimeType.IMAGE_PNG -> "PNG" + ImageMimeType.IMAGE_WEBP -> "WEBP" + } + val filename = if(existingExtension.isBlank()) "${downloadedImage.name}.$extension" else downloadedImage.name + try { + val downloadDestinationFolder = Path.of(postDownloadState.downloadDirectory) + synchronized(downloadDestinationFolder.pathString.intern()) { + Files.createDirectories(downloadDestinationFolder) + } + val image = downloadDestinationFolder.resolve( + "${ + if (settings.downloadSettings.forceOrder) String.format( + "%03d_", index + 1 + ) else "" + }$filename" + ) + Files.copy(downloadedImage.path, image, StandardCopyOption.REPLACE_EXISTING) + } catch (e: Exception) { + throw HostException("Failed to rename the image", e) + } finally { + try { + Files.delete(downloadedImage.path) + } catch (e: IOException) { + log.warn( + "Failed to delete temporary file ${downloadedImage.path}" + ) + } + } + } + + @Throws(Exception::class) + override fun run() { + if (stopped) { + return + } + download() + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || javaClass != other.javaClass) return false + val that = other as ImageDownloadRunnable + return imageInternalId == that.imageInternalId + } + + override fun hashCode(): Int { + return Objects.hash(imageInternalId) + } + + fun stop() { + stopped = true + val contextAttributes = httpContext.getAttribute( + HTTPService.ContextAttributes.CONTEXT_ATTRIBUTES, HTTPService.ContextAttributes::class.java + ) + if (contextAttributes != null) { + synchronized(contextAttributes.requests) { + for (request in contextAttributes.requests) { + request.abort() + } + } + } + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/download/PostDownloadRunnable.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/download/PostDownloadRunnable.kt new file mode 100644 index 00000000..7f4f4bfa --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/download/PostDownloadRunnable.kt @@ -0,0 +1,162 @@ +package me.mnlr.vripper.download + +import net.jodah.failsafe.Failsafe +import net.jodah.failsafe.function.CheckedSupplier +import org.apache.http.client.HttpClient +import org.apache.http.client.methods.CloseableHttpResponse +import org.apache.http.client.methods.HttpGet +import org.apache.http.client.protocol.HttpClientContext +import org.apache.http.client.utils.URIBuilder +import org.apache.http.util.EntityUtils +import me.mnlr.vripper.SpringContext +import me.mnlr.vripper.delegate.LoggerDelegate +import me.mnlr.vripper.entities.LogEvent +import me.mnlr.vripper.entities.LogEvent.Status.* +import me.mnlr.vripper.exception.DownloadException +import me.mnlr.vripper.exception.PostParseException +import me.mnlr.vripper.formatToString +import me.mnlr.vripper.model.PostItem +import me.mnlr.vripper.parser.ThreadLookupAPIResponseHandler +import me.mnlr.vripper.repositories.LogEventRepository +import me.mnlr.vripper.services.* +import java.net.URISyntaxException +import java.util.* +import javax.xml.parsers.SAXParserFactory + +class PostDownloadRunnable(private val threadId: String, private val postId: String) : Runnable { + private val log by LoggerDelegate() + private val dataTransaction: DataTransaction = + SpringContext.getBean(DataTransaction::class.java) + private val settingsService: SettingsService = + SpringContext.getBean(SettingsService::class.java) + private val vgAuthService: VGAuthService = SpringContext.getBean(VGAuthService::class.java) + private val eventRepository: LogEventRepository = + SpringContext.getBean(LogEventRepository::class.java) + private val cm: HTTPService = SpringContext.getBean(HTTPService::class.java) + private val retryPolicyService: RetryPolicyService = + SpringContext.getBean(RetryPolicyService::class.java) + private val downloadService: DownloadService = + SpringContext.getBean(DownloadService::class.java) + private val link: String = + "${settingsService.settings.viperSettings.host}/threads/$threadId?p=$postId" + private val logEvent: LogEvent + + init { + logEvent = eventRepository.save( + LogEvent( + type = LogEvent.Type.POST, + status = PENDING, + message = "Processing $link" + ) + ) + } + + override fun run() { + try { + eventRepository.update(logEvent.copy(status = PROCESSING)) + if (dataTransaction.exists(postId)) { + log.warn(String.format("skipping %s, already loaded", postId)) + eventRepository.update( + logEvent.copy( + status = ERROR, + message = String.format("Gallery %s is already loaded", link) + ) + ) + return + } + val postItem: PostItem = try { + parse() + } catch (e: PostParseException) { + val error = String.format("parsing failed for gallery %s", link) + log.error(error, e) + eventRepository.update( + logEvent.copy( + status = ERROR, message = """ + $error + ${e.formatToString()} + """.trimIndent() + ) + ) + return + } + if (postItem.imageItemList.isEmpty()) { + val error = String.format("Gallery %s contains no images to download", link) + log.error(error) + eventRepository.update(logEvent.copy(status = ERROR, message = error)) + return + } + val post = dataTransaction.newPost(postItem) + vgAuthService.leaveThanks(post) +// metadataService.startFetchingMetadata(post) + if (settingsService.settings.downloadSettings.autoStart) { + log.debug("Auto start downloads option is enabled") + downloadService.restartAll(listOf(postItem.postId)) + log.debug(String.format("Done enqueuing jobs for %s", postItem.url)) + } + + eventRepository.update( + logEvent.copy( + status = DONE, + message = String.format( + "Gallery %s is successfully added to download queue", + link + ) + ) + ) + } catch (e: Exception) { + val error = String.format("Error when adding gallery %s", link) + log.error(error, e) + eventRepository.update( + logEvent.copy( + status = ERROR, message = """ + $error + ${e.formatToString()} + """.trimIndent() + ) + ) + } + } + + @Throws(PostParseException::class) + fun parse(): PostItem { + log.debug("Parsing post $postId") + val httpGet: HttpGet = try { + val uriBuilder = URIBuilder("${settingsService.settings.viperSettings.host}/vr.php") + uriBuilder.setParameter("p", postId) + cm.buildHttpGet(uriBuilder.build(), HttpClientContext.create()) + } catch (e: URISyntaxException) { + throw PostParseException(e) + } + val threadLookupAPIResponseHandler = ThreadLookupAPIResponseHandler() + log.debug("Requesting $httpGet") + return try { + Failsafe.with(retryPolicyService.buildGenericRetryPolicy()).onFailure { + log.error( + "parsing failed for thread $threadId, post $postId", it.failure + ) + }.get(CheckedSupplier { + val connection: HttpClient = cm.client.build() + (connection.execute( + httpGet, vgAuthService.context + ) as CloseableHttpResponse).use { response -> + try { + if (response.statusLine.statusCode / 100 != 2) { + throw DownloadException("Unexpected response code '${response.statusLine.statusCode}' for $httpGet") + } + factory.newSAXParser() + .parse(response.entity.content, threadLookupAPIResponseHandler) + threadLookupAPIResponseHandler.result.postItemList[0] + } finally { + EntityUtils.consumeQuietly(response.entity) + } + } + }) + } catch (e: Exception) { + throw PostParseException(e) + } + } + + companion object { + private val factory = SAXParserFactory.newInstance() + } +} diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/entities/ImageDownloadState.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/entities/ImageDownloadState.kt new file mode 100644 index 00000000..6ccbf9e5 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/entities/ImageDownloadState.kt @@ -0,0 +1,31 @@ +package me.mnlr.vripper.entities + +import me.mnlr.vripper.entities.domain.Status +import me.mnlr.vripper.host.Host + +data class ImageDownloadState( + var id: Long? = null, + val postId: String, + val url: String, + val host: Host, + val index: Int, + val postIdRef: Long, + var total: Long = -1, + var current: Long = 0, + var status: Status = Status.STOPPED, +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as ImageDownloadState + + if (url != other.url) return false + + return true + } + + override fun hashCode(): Int { + return url.hashCode() + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/entities/LogEvent.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/entities/LogEvent.kt new file mode 100644 index 00000000..b04e52c1 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/entities/LogEvent.kt @@ -0,0 +1,22 @@ +package me.mnlr.vripper.entities + +import java.time.LocalDateTime + +data class LogEvent( + val id: Long? = null, + val type: Type, + val status: Status, + val time: LocalDateTime = LocalDateTime.now(), + val message: String, +) { + + enum class Type(val stringValue: String) { + POST("Post"), THREAD("Thread"), THANKS("Thanks"), METADATA("Metadata"), SCAN("Scan"), DOWNLOAD( + "Download" + ) + } + + enum class Status(val stringValue: String) { + PENDING("Pending"), PROCESSING("Processing"), DONE("Done"), ERROR("Error") + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/entities/Metadata.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/entities/Metadata.kt new file mode 100644 index 00000000..4b2ca78e --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/entities/Metadata.kt @@ -0,0 +1,19 @@ +package me.mnlr.vripper.entities + +class Metadata { + var postIdRef: Long? = null + var postId: String? = null + var postedBy: String? = null + var resolvedNames = emptyList() + + companion object { + fun from(metadata: Metadata): Metadata { + val copy = Metadata() + copy.postIdRef = metadata.postIdRef + copy.postId = metadata.postId + copy.postedBy = metadata.postedBy + copy.resolvedNames = metadata.resolvedNames + return copy + } + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/entities/PostDownloadState.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/entities/PostDownloadState.kt new file mode 100644 index 00000000..80cd4727 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/entities/PostDownloadState.kt @@ -0,0 +1,37 @@ +package me.mnlr.vripper.entities + +import me.mnlr.vripper.entities.domain.Status +import java.time.LocalDateTime + +data class PostDownloadState( + var id: Long? = null, + val postTitle: String, + val threadTitle: String, + val forum: String, + val url: String, + val token: String, + val postId: String, + val threadId: String, + val total: Int, + val hosts: Set, + val downloadDirectory: String, + val addedOn: LocalDateTime = LocalDateTime.now(), + var status: Status = Status.STOPPED, + var done: Int = 0, + var rank: Int = Int.MAX_VALUE +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as PostDownloadState + + if (postId != other.postId) return false + + return true + } + + override fun hashCode(): Int { + return postId.hashCode() + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/entities/Thread.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/entities/Thread.kt new file mode 100644 index 00000000..ae9e35e4 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/entities/Thread.kt @@ -0,0 +1,21 @@ +package me.mnlr.vripper.entities + +data class Thread( + val id: Long? = null, + val link: String, + val threadId: String, + var total: Int = 0, +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as Thread + + return threadId == other.threadId + } + + override fun hashCode(): Int { + return threadId.hashCode() + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/entities/domain/Status.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/entities/domain/Status.kt new file mode 100644 index 00000000..aed4a6f9 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/entities/domain/Status.kt @@ -0,0 +1,5 @@ +package me.mnlr.vripper.entities.domain + +enum class Status(val stringValue: String) { + PENDING("Pending"), DOWNLOADING("Downloading"), FINISHED("Finished"), ERROR("Error"), STOPPED("Stopped") +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/event/Event.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/event/Event.kt new file mode 100644 index 00000000..74da2272 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/event/Event.kt @@ -0,0 +1,7 @@ +package me.mnlr.vripper.event + +data class Event(val kind: Kind, val data: T) { + enum class Kind { + POST_UPDATE, POST_REMOVE, IMAGE_UPDATE, METADATA_UPDATE, THREAD_UPDATE, THREAD_REMOVE, LOG_EVENT_UPDATE, LOG_EVENT_REMOVE, VG_USER, DOWNLOAD_STATUS, BYTES_PER_SECOND, SETTINGS_UPDATE + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/event/EventBus.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/event/EventBus.kt new file mode 100644 index 00000000..164c7072 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/event/EventBus.kt @@ -0,0 +1,30 @@ +package me.mnlr.vripper.event + +import jakarta.annotation.PreDestroy +import org.springframework.stereotype.Service +import reactor.core.publisher.Flux +import reactor.core.publisher.SignalType +import reactor.core.publisher.Sinks +import reactor.core.publisher.Sinks.EmitFailureHandler +import reactor.core.publisher.Sinks.EmitResult + +@Service +class EventBus { + private val sink = Sinks.many().multicast().onBackpressureBuffer>() + fun publishEvent(event: Event<*>) { + sink.emitNext(event, RETRY) + } + + fun flux(): Flux> { + return sink.asFlux() + } + + @PreDestroy + private fun destroy() { + sink.emitComplete(RETRY) + } + + companion object { + val RETRY = EmitFailureHandler { _: SignalType?, _: EmitResult? -> true } + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/exception/DownloadException.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/exception/DownloadException.kt new file mode 100644 index 00000000..d64ebe72 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/exception/DownloadException.kt @@ -0,0 +1,6 @@ +package me.mnlr.vripper.exception + +class DownloadException : Exception { + constructor(message: String?) : super(message) + constructor(e: Throwable?) : super(e) +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/exception/HostException.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/exception/HostException.kt new file mode 100644 index 00000000..3d0e18ce --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/exception/HostException.kt @@ -0,0 +1,7 @@ +package me.mnlr.vripper.exception + +class HostException : Exception { + constructor(e: Throwable?) : super(e) + constructor(message: String?) : super(message) + constructor(message: String?, e: Throwable?) : super(message, e) +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/exception/HtmlProcessorException.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/exception/HtmlProcessorException.kt new file mode 100644 index 00000000..aa7da06d --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/exception/HtmlProcessorException.kt @@ -0,0 +1,3 @@ +package me.mnlr.vripper.exception + +class HtmlProcessorException(e: Throwable?) : Exception(e) \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/exception/PostParseException.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/exception/PostParseException.kt new file mode 100644 index 00000000..ab73809a --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/exception/PostParseException.kt @@ -0,0 +1,7 @@ +package me.mnlr.vripper.exception + +class PostParseException : Exception { + constructor(message: String?) : super(message) + constructor(message: String?, e: Throwable?) : super(message, e) + constructor(e: Throwable?) : super(e) +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/exception/QueueException.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/exception/QueueException.kt new file mode 100644 index 00000000..db37cb5f --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/exception/QueueException.kt @@ -0,0 +1,7 @@ +package me.mnlr.vripper.exception + +class QueueException : Exception { + constructor(message: String?) : super(message) + constructor(message: String?, e: Throwable?) : super(message, e) + constructor(e: Throwable?) : super(e) +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/exception/RenameException.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/exception/RenameException.kt new file mode 100644 index 00000000..888de728 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/exception/RenameException.kt @@ -0,0 +1,3 @@ +package me.mnlr.vripper.exception + +class RenameException(message: String?, e: Exception?) : Exception(message, e) \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/exception/ValidationException.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/exception/ValidationException.kt new file mode 100644 index 00000000..38197662 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/exception/ValidationException.kt @@ -0,0 +1,3 @@ +package me.mnlr.vripper.exception + +class ValidationException(message: String) : Exception(message) \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/exception/VripperException.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/exception/VripperException.kt new file mode 100644 index 00000000..d57cae2c --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/exception/VripperException.kt @@ -0,0 +1,6 @@ +package me.mnlr.vripper.exception + +class VripperException : Exception { + constructor(message: String?) : super(message) + constructor(e: Throwable?) : super(e) +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/exception/XpathException.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/exception/XpathException.kt new file mode 100644 index 00000000..1dbb6432 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/exception/XpathException.kt @@ -0,0 +1,3 @@ +package me.mnlr.vripper.exception + +class XpathException(e: Throwable?) : Exception(e) \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/host/AcidimgHost.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/host/AcidimgHost.kt new file mode 100644 index 00000000..69dd8c54 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/host/AcidimgHost.kt @@ -0,0 +1,104 @@ +package me.mnlr.vripper.host + +import org.apache.http.NameValuePair +import org.apache.http.client.HttpClient +import org.apache.http.client.entity.UrlEncodedFormEntity +import org.apache.http.client.methods.CloseableHttpResponse +import org.apache.http.message.BasicNameValuePair +import org.apache.http.util.EntityUtils +import org.springframework.stereotype.Service +import org.w3c.dom.Document +import org.w3c.dom.Node +import me.mnlr.vripper.delegate.LoggerDelegate +import me.mnlr.vripper.download.ImageDownloadContext +import me.mnlr.vripper.exception.HostException +import me.mnlr.vripper.exception.XpathException +import me.mnlr.vripper.services.* + +@Service +class AcidimgHost( + private val httpService: HTTPService, + private val htmlProcessorService: HtmlProcessorService, + private val xpathService: XpathService, + dataTransaction: DataTransaction, + downloadSpeedService: DownloadSpeedService, +) : Host(httpService, htmlProcessorService, dataTransaction, downloadSpeedService) { + private val log by LoggerDelegate() + override val host: String + get() = Companion.host + + @Throws(HostException::class) + override fun resolve( + url: String, + document: Document, + context: ImageDownloadContext + ): Pair { + try { + log.debug( + String.format( + "Looking for xpath expression %s in %s", + CONTINUE_BUTTON_XPATH, + url + ) + ) + xpathService.getAsNode(document, CONTINUE_BUTTON_XPATH) + } catch (e: XpathException) { + throw HostException(e) + } + log.debug(String.format("Click button found for %s", url)) + val client: HttpClient = httpService.client.build() + val httpPost = httpService.buildHttpPost(url, context.httpContext) + httpPost.addHeader("Referer", url) + val params: MutableList = ArrayList() + params.add(BasicNameValuePair("imgContinue", "Continue to your image")) + try { + httpPost.entity = UrlEncodedFormEntity(params) + } catch (e: Exception) { + throw HostException(e) + } + log.debug(String.format("Requesting %s", httpPost)) + val doc = try { + (client.execute( + httpPost, context.httpContext + ) as CloseableHttpResponse).use { response -> + log.debug(String.format("Cleaning response for %s", httpPost)) + try { + htmlProcessorService.clean(response.entity.content) + } finally { + EntityUtils.consumeQuietly(response.entity) + } + } + } catch (e: Exception) { + throw HostException(e) + } + val imgNode: Node = try { + log.debug(String.format("Looking for xpath expression %s in %s", IMG_XPATH, url)) + xpathService.getAsNode(doc, IMG_XPATH) + } catch (e: XpathException) { + throw HostException(e) + } ?: throw HostException( + String.format( + "Xpath '%s' cannot be found in '%s'", + IMG_XPATH, + url + ) + ) + return try { + log.debug(String.format("Resolving name and image url for %s", url)) + val imgTitle = imgNode.attributes.getNamedItem("alt").textContent.trim() + val imgUrl = imgNode.attributes.getNamedItem("src").textContent.trim() + Pair( + imgTitle.ifEmpty { getDefaultImageName(imgUrl) }, imgUrl + ) + } catch (e: Exception) { + throw HostException("Unexpected error occurred", e) + } + } + + companion object { + private const val host = "acidimg.cc" + private const val lookup = "acidimg.cc" + private const val CONTINUE_BUTTON_XPATH = "//input[@id='continuebutton']" + private const val IMG_XPATH = "//img[@class='centred']" + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/host/DPicMeHost.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/host/DPicMeHost.kt new file mode 100644 index 00000000..910b1a72 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/host/DPicMeHost.kt @@ -0,0 +1,69 @@ +package me.mnlr.vripper.host + +import org.springframework.stereotype.Service +import org.w3c.dom.Document +import org.w3c.dom.Node +import me.mnlr.vripper.delegate.LoggerDelegate +import me.mnlr.vripper.download.ImageDownloadContext +import me.mnlr.vripper.exception.HostException +import me.mnlr.vripper.exception.XpathException +import me.mnlr.vripper.services.* +import java.util.* + +@Service +class DPicMeHost( + private val xpathService: XpathService, + httpService: HTTPService, + htmlProcessorService: HtmlProcessorService, + dataTransaction: DataTransaction, + downloadSpeedService: DownloadSpeedService, +) : Host(httpService, htmlProcessorService, dataTransaction, downloadSpeedService) { + private val log by LoggerDelegate() + override val host: String + get() = Companion.host + + @Throws(HostException::class) + override fun resolve( + url: String, + document: Document, + context: ImageDownloadContext + ): Pair { + val imgNode: Node = try { + log.debug(String.format("Looking for xpath expression %s in %s", IMG_XPATH, url)) + xpathService.getAsNode(document, IMG_XPATH) + } catch (e: XpathException) { + throw HostException(e) + } ?: throw HostException( + String.format( + "Xpath '%s' cannot be found in '%s'", + IMG_XPATH, + url + ) + ) + return try { + log.debug(String.format("Resolving name and image url for %s", url)) + val imgTitle = + Optional.ofNullable(imgNode.attributes.getNamedItem("alt")) + .map { e: Node -> e.textContent.trim() } + .orElse("") + val imgUrl = + Optional.ofNullable(imgNode.attributes.getNamedItem("src")) + .map { e: Node -> e.textContent.trim() } + .orElse("") + var defaultName: String = UUID.randomUUID().toString() + val index = imgUrl.lastIndexOf('/') + if (index != -1 && index < imgUrl.length) { + defaultName = imgUrl.substring(imgUrl.lastIndexOf('/') + 1) + } + Pair((imgTitle.ifEmpty { defaultName })!!, imgUrl) + } catch (e: Exception) { + throw HostException("Unexpected error occurred", e) + } + } + + companion object { + private const val host = "dpic.me" + private const val lookup = "dpic.me" + private const val IMG_XPATH = "//img[@id='pic']" + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/host/Host.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/host/Host.kt new file mode 100644 index 00000000..ae442a2d --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/host/Host.kt @@ -0,0 +1,229 @@ +package me.mnlr.vripper.host + +import org.apache.http.Header +import org.apache.http.client.HttpClient +import org.apache.http.client.methods.CloseableHttpResponse +import org.apache.http.client.protocol.HttpClientContext +import org.apache.http.util.EntityUtils +import org.w3c.dom.Document +import me.mnlr.vripper.delegate.LoggerDelegate +import me.mnlr.vripper.download.ImageDownloadContext +import me.mnlr.vripper.exception.DownloadException +import me.mnlr.vripper.exception.HostException +import me.mnlr.vripper.getFileNameWithoutExtension +import me.mnlr.vripper.services.* +import java.io.FileOutputStream +import java.nio.file.Files +import java.nio.file.Path +import kotlin.io.path.pathString + +abstract class Host( + private val httpService: HTTPService, + private val htmlProcessorService: HtmlProcessorService, + private val dataTransaction: DataTransaction, + private val downloadSpeedService: DownloadSpeedService +) { + private val log by LoggerDelegate() + + companion object { + private const val READ_BUFFER_SIZE = 8192 + } + + abstract val host: String + + @Throws(HostException::class) + abstract fun resolve( + url: String, + document: Document, + context: ImageDownloadContext + ): Pair + + @Throws(HostException::class) + fun downloadInternal(url: String, context: ImageDownloadContext): DownloadedImage { + val headers = head(url, context.httpContext) + // is the body of type image ? + val imageMimeType = getImageMimeType(headers) + + val downloadedImage = if (imageMimeType != null) { + // a direct link, awesome + val downloadedImage = fetch(url, context) { + handleImageDownload(it, context) + } + DownloadedImage(getDefaultImageName(url), downloadedImage.first, downloadedImage.second) + } else { + // linked image ? + val value = headers.find { it.name.contains("content-type", true) }?.value + if (value != null) { + if (value.contains("text/html")) { + val document = fetch(url, context) { + htmlProcessorService.clean(it.entity.content) + } + if (log.isDebugEnabled) { + log.debug("Cleaning $url response", url) + } + val resolvedImage = resolve(url, document, context) + val downloadImage: Pair = + fetch(resolvedImage.second, context) { + handleImageDownload(it, context) + } + DownloadedImage(resolvedImage.first, downloadImage.first, downloadImage.second) + } else { + throw HostException("Unable to download $url, can't process content type $value") + } + } else { + throw HostException("Unexpected server response for $url, response have no content type") + } + } + return downloadedImage + } + + private fun handleImageDownload( + response: CloseableHttpResponse, + context: ImageDownloadContext + ): Pair { + val mimeType = getImageMimeType(response.allHeaders) + ?: throw HostException("Unsupported image type ${response.getFirstHeader("content-type")}") + + val tempImage = Files.createTempFile("vripper", "tmp") + return response.entity.content.use { inputStream -> + try { + FileOutputStream(tempImage.pathString).use { fos -> + val image = context.image + image.total = response.entity.contentLength + dataTransaction.update(image) + log.debug( + "Length is ${image.total}" + ) + log.debug( + "Starting data transfer" + ) + val buffer = ByteArray(READ_BUFFER_SIZE) + var read: Int + while (inputStream.read(buffer, 0, READ_BUFFER_SIZE) + .also { read = it } != -1 && !context.stopped + ) { + fos.write(buffer, 0, read) + with(image) { + current += read + } + downloadSpeedService.increase(read.toLong()) + dataTransaction.update(image) + } + Pair(tempImage, mimeType) + } + } finally { + EntityUtils.consumeQuietly(response.entity) + } + } + } + + fun isSupported(url: String): Boolean { + return url.contains(host) + } + + @Throws(HostException::class) + fun head(url: String, context: HttpClientContext): Array
{ + val client: HttpClient = httpService.client.build() + val httpGet = httpService.buildHttpHead(url, context) + log.debug(String.format("Requesting %s", url)) + return try { + (client.execute( + httpGet, + context + ) as CloseableHttpResponse).use { response -> + try { + if (response.statusLine.statusCode / 100 != 2) { + throw HostException( + String.format( + "Unexpected response code: %d", response.statusLine.statusCode + ) + ) + } + response.allHeaders + } finally { + EntityUtils.consumeQuietly(response.entity) + } + } + } catch (e: Exception) { + throw HostException(e) + } + } + + @Throws(HostException::class) + fun fetch( + url: String, + context: ImageDownloadContext, + transformer: (CloseableHttpResponse) -> T + ): T { + val client: HttpClient = httpService.client.build() + val httpGet = httpService.buildHttpGet(url, context.httpContext) + httpGet.addHeader("Referer", context.image.url) + log.debug(String.format("Requesting %s", url)) + return try { + (client.execute( + httpGet, + context.httpContext + ) as CloseableHttpResponse).use { response -> + if (response.statusLine.statusCode / 100 != 2) { + EntityUtils.consumeQuietly(response.entity) + throw DownloadException( + "Server returned code ${response.statusLine.statusCode}" + ) + } + try { + transformer(response) + } finally { + EntityUtils.consumeQuietly(response.entity) + } + } + } catch (e: Exception) { + throw HostException(e) + } + } + + private fun getImageMimeType(headers: Array
): ImageMimeType? { + + // first check if content type header exists + val value = headers.find { it.name.contains("content-type", true) }?.value + + // header found, check the type + return if (value != null) { + ImageMimeType.values().find { + value.contains(it.strValue, true) + } + } else { + null + } + } + + fun getDefaultImageName(imgUrl: String): String { + val imageTitle = imgUrl.substring(imgUrl.lastIndexOf('/') + 1) + log.debug(String.format("Extracting name from url %s: %s", imgUrl, imageTitle)) + return getFileNameWithoutExtension(imageTitle) + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as Host + + if (host != other.host) return false + + return true + } + + override fun hashCode(): Int { + return host.hashCode() + } +} + +data class DownloadedImage(val name: String, val path: Path, val type: ImageMimeType) + +enum class ImageMimeType(val strValue: String) { + IMAGE_BMP("image/bmp"), + IMAGE_GIF("image/gif"), + IMAGE_JPEG("image/jpeg"), + IMAGE_PNG("image/png"), + IMAGE_WEBP("image/webp"), +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/host/ImageBamHost.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/host/ImageBamHost.kt new file mode 100644 index 00000000..1a7e79f5 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/host/ImageBamHost.kt @@ -0,0 +1,92 @@ +package me.mnlr.vripper.host + +import org.apache.http.impl.cookie.BasicClientCookie +import org.springframework.stereotype.Service +import org.w3c.dom.Document +import org.w3c.dom.Node +import me.mnlr.vripper.delegate.LoggerDelegate +import me.mnlr.vripper.download.ImageDownloadContext +import me.mnlr.vripper.exception.HostException +import me.mnlr.vripper.exception.XpathException +import me.mnlr.vripper.services.* +import java.sql.Date +import java.time.LocalDateTime +import java.time.ZoneId +import java.util.* + +@Service +class ImageBamHost( + private val htmlProcessorService: HtmlProcessorService, + private val xpathService: XpathService, + httpService: HTTPService, + dataTransaction: DataTransaction, + downloadSpeedService: DownloadSpeedService, +) : Host(httpService, htmlProcessorService, dataTransaction, downloadSpeedService) { + private val log by LoggerDelegate() + override val host: String + get() = Companion.host + + @Throws(HostException::class) + override fun resolve( + url: String, + document: Document, + context: ImageDownloadContext + ): Pair { + val doc = try { + log.debug(String.format("Looking for xpath expression %s in %s", CONTINUE_XPATH, url)) + if (xpathService.getAsNode(document, CONTINUE_XPATH) != null) { + val clientCookie = BasicClientCookie("nsfw_inter", "1") + clientCookie.domain = "www.imagebam.com" + clientCookie.path = "/" + clientCookie.expiryDate = + Date.from( + LocalDateTime.now().plusDays(3).atZone(ZoneId.systemDefault()).toInstant() + ) + context.httpContext.cookieStore.addCookie(clientCookie) + fetch(url, context) { + htmlProcessorService.clean(it.entity.content) + } + } else { + document + } + } catch (e: XpathException) { + throw HostException(e) + } + val imgNode: Node = try { + log.debug(String.format("Looking for xpath expression %s in %s", IMG_XPATH, url)) + xpathService.getAsNode(doc, IMG_XPATH) + } catch (e: XpathException) { + throw HostException(e) + } ?: throw HostException( + String.format( + "Xpath '%s' cannot be found in '%s'", + IMG_XPATH, + url + ) + ) + return try { + log.debug(String.format("Resolving name and image url for %s", url)) + val imgTitle = Optional.ofNullable(imgNode.attributes.getNamedItem("alt")) + .map { e: Node -> e.textContent.trim { it <= ' ' } } + .orElse("") + val imgUrl = Optional.ofNullable(imgNode.attributes.getNamedItem("src")) + .map { e: Node -> e.textContent.trim { it <= ' ' } } + .orElse("") + var defaultName: String = UUID.randomUUID().toString() + val index = imgUrl.lastIndexOf('/') + if (index != -1 && index < imgUrl.length) { + defaultName = imgUrl.substring(imgUrl.lastIndexOf('/') + 1) + } + Pair((imgTitle.ifEmpty { defaultName })!!, imgUrl) + } catch (e: Exception) { + throw HostException("Unexpected error occurred", e) + } + } + + companion object { + private const val host = "imagebam.com" + private const val lookup = "imagebam.com" + private const val IMG_XPATH = "//img[contains(@class,'main-image')]" + private const val CONTINUE_XPATH = "//*[contains(text(), 'Continue')]" + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/host/ImageTwistHost.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/host/ImageTwistHost.kt new file mode 100644 index 00000000..3ce874be --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/host/ImageTwistHost.kt @@ -0,0 +1,61 @@ +package me.mnlr.vripper.host + +import org.springframework.stereotype.Service +import org.w3c.dom.Document +import org.w3c.dom.Node +import me.mnlr.vripper.delegate.LoggerDelegate +import me.mnlr.vripper.download.ImageDownloadContext +import me.mnlr.vripper.exception.HostException +import me.mnlr.vripper.exception.XpathException +import me.mnlr.vripper.services.* +import java.util.* + +@Service +class ImageTwistHost( + private val xpathService: XpathService, + httpService: HTTPService, + htmlProcessorService: HtmlProcessorService, + dataTransaction: DataTransaction, + downloadSpeedService: DownloadSpeedService, +) : Host(httpService, htmlProcessorService, dataTransaction, downloadSpeedService) { + private val log by LoggerDelegate() + override val host: String + get() = Companion.host + + @Throws(HostException::class) + override fun resolve( + url: String, + document: Document, + context: ImageDownloadContext + ): Pair { + val imgNode: Node = try { + log.debug(String.format("Looking for xpath expression %s in %s", IMG_XPATH, url)) + xpathService.getAsNode(document, IMG_XPATH) + } catch (e: XpathException) { + throw HostException(e) + } ?: throw HostException( + String.format( + "Xpath '%s' cannot be found in '%s'", + IMG_XPATH, + url + ) + ) + return try { + log.debug(String.format("Resolving name and image url for %s", url)) + val imgTitle = + Optional.ofNullable(imgNode.attributes.getNamedItem("alt")) + .map { obj: Node -> obj.textContent } + .map { obj: String -> obj.trim { it <= ' ' } }.orElse(null) + val imgUrl = imgNode.attributes.getNamedItem("src").textContent.trim { it <= ' ' } + Pair(imgTitle!!, imgUrl) + } catch (e: Exception) { + throw HostException("Unexpected error occurred", e) + } + } + + companion object { + private const val host = "imagetwist.com" + private const val lookup = "imagetwist.com" + private const val IMG_XPATH = "//img[contains(@class, 'img')]" + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/host/ImageVenueHost.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/host/ImageVenueHost.kt new file mode 100644 index 00000000..0fb49d43 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/host/ImageVenueHost.kt @@ -0,0 +1,80 @@ +package me.mnlr.vripper.host + +import org.springframework.stereotype.Service +import org.w3c.dom.Document +import org.w3c.dom.Node +import me.mnlr.vripper.delegate.LoggerDelegate +import me.mnlr.vripper.download.ImageDownloadContext +import me.mnlr.vripper.exception.HostException +import me.mnlr.vripper.exception.XpathException +import me.mnlr.vripper.services.* + +@Service +class ImageVenueHost( + private val htmlProcessorService: HtmlProcessorService, + private val xpathService: XpathService, + httpService: HTTPService, + dataTransaction: DataTransaction, + downloadSpeedService: DownloadSpeedService, +) : Host(httpService, htmlProcessorService, dataTransaction, downloadSpeedService) { + private val log by LoggerDelegate() + override val host: String + get() = Companion.host + + @Throws(HostException::class) + override fun resolve( + url: String, + document: Document, + context: ImageDownloadContext + ): Pair { + val doc = try { + log.debug( + String.format( + "Looking for xpath expression %s in %s", + CONTINUE_BUTTON_XPATH, + url + ) + ) + if (xpathService.getAsNode(document, CONTINUE_BUTTON_XPATH) != null) { + // Button detected. No need to actually click it, just make the call again. + fetch(url, context) { + htmlProcessorService.clean(it.entity.content) + } + } else { + document + } + } catch (e: XpathException) { + throw HostException(e) + } + val imgNode: Node = try { + log.debug(String.format("Looking for xpath expression %s in %s", IMG_XPATH, url)) + xpathService.getAsNode(doc, IMG_XPATH) + } catch (e: XpathException) { + throw HostException(e) + } ?: throw HostException( + String.format( + "Xpath '%s' cannot be found in '%s'", + IMG_XPATH, + url + ) + ) + return try { + log.debug(String.format("Resolving name and image url for %s", url)) + val imgTitle = imgNode.attributes.getNamedItem("alt").textContent.trim { it <= ' ' } + val imgUrl = imgNode.attributes.getNamedItem("src").textContent.trim { it <= ' ' } + Pair( + if (imgTitle.isEmpty()) imgUrl.substring(imgUrl.lastIndexOf('/') + 1) else imgTitle, + imgUrl + ) + } catch (e: Exception) { + throw HostException("Unexpected error occurred", e) + } + } + + companion object { + private const val host = "imagevenue.com" + private const val lookup = "imagevenue.com" + private const val CONTINUE_BUTTON_XPATH = "//a[@title='Continue to ImageVenue']" + private const val IMG_XPATH = "//a[@data-toggle='full']/img" + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/host/ImageZillaHost.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/host/ImageZillaHost.kt new file mode 100644 index 00000000..29e03763 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/host/ImageZillaHost.kt @@ -0,0 +1,59 @@ +package me.mnlr.vripper.host + +import org.springframework.stereotype.Service +import org.w3c.dom.Document +import me.mnlr.vripper.delegate.LoggerDelegate +import me.mnlr.vripper.download.ImageDownloadContext +import me.mnlr.vripper.exception.HostException +import me.mnlr.vripper.exception.XpathException +import me.mnlr.vripper.services.* + +@Service +class ImageZillaHost( + private val xpathService: XpathService, + httpService: HTTPService, + htmlProcessorService: HtmlProcessorService, + dataTransaction: DataTransaction, + downloadSpeedService: DownloadSpeedService, +) : Host(httpService, htmlProcessorService, dataTransaction, downloadSpeedService) { + private val log by LoggerDelegate() + override val host: String + get() = Companion.host + + @Throws(HostException::class) + override fun resolve( + url: String, + document: Document, + context: ImageDownloadContext + ): Pair { + val titleNode = try { + log.debug(String.format("Looking for xpath expression %s in %s", IMG_XPATH, url)) + xpathService.getAsNode(document, IMG_XPATH) + } catch (e: XpathException) { + throw HostException(e) + } ?: throw HostException( + String.format( + "Xpath '%s' cannot be found in '%s'", + IMG_XPATH, + url + ) + ) + log.debug(String.format("Resolving name for %s", url)) + var title = titleNode.attributes.getNamedItem("title").textContent.trim() + titleNode.textContent.trim() + if (title.isEmpty()) { + title = getDefaultImageName(url) + } + return try { + Pair(title, url.replace("show", "images")) + } catch (e: Exception) { + throw HostException("Unexpected error occurred", e) + } + } + + companion object { + private const val host = "imagezilla.net" + private const val lookup = "imagezilla.net/show" + private const val IMG_XPATH = "//img[@id='photo']" + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/host/ImgSpiceHost.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/host/ImgSpiceHost.kt new file mode 100644 index 00000000..9c353691 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/host/ImgSpiceHost.kt @@ -0,0 +1,59 @@ +package me.mnlr.vripper.host + +import org.springframework.stereotype.Service +import org.w3c.dom.Document +import org.w3c.dom.Node +import me.mnlr.vripper.delegate.LoggerDelegate +import me.mnlr.vripper.download.ImageDownloadContext +import me.mnlr.vripper.exception.HostException +import me.mnlr.vripper.exception.XpathException +import me.mnlr.vripper.services.* + +@Service +class ImgSpiceHost( + private val xpathService: XpathService, + httpService: HTTPService, + htmlProcessorService: HtmlProcessorService, + dataTransaction: DataTransaction, + downloadSpeedService: DownloadSpeedService, +) : Host(httpService, htmlProcessorService, dataTransaction, downloadSpeedService) { + private val log by LoggerDelegate() + override val host: String + get() = Companion.host + + @Throws(HostException::class) + override fun resolve( + url: String, + document: Document, + context: ImageDownloadContext + ): Pair { + val imgNode: Node = try { + log.debug(String.format("Looking for xpath expression %s in %s", IMG_XPATH, url)) + xpathService.getAsNode(document, IMG_XPATH) + } catch (e: XpathException) { + throw HostException(e) + } ?: throw HostException( + String.format( + "Xpath '%s' cannot be found in '%s'", + IMG_XPATH, + url + ) + ) + return try { + log.debug(String.format("Resolving name and image url for %s", url)) + val imgTitle = imgNode.attributes.getNamedItem("alt").textContent.trim { it <= ' ' } + val imgUrl = imgNode.attributes.getNamedItem("src").textContent.trim { it <= ' ' } + Pair( + imgTitle.ifEmpty { imgUrl.substring(imgUrl.lastIndexOf('/') + 1) }, imgUrl + ) + } catch (e: Exception) { + throw HostException("Unexpected error occurred", e) + } + } + + companion object { + private const val host = "imgspice.com" + private const val lookup = "imgspice.com" + private const val IMG_XPATH = "//img[@id='imgpreview']" + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/host/ImgboxHost.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/host/ImgboxHost.kt new file mode 100644 index 00000000..09af3f13 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/host/ImgboxHost.kt @@ -0,0 +1,57 @@ +package me.mnlr.vripper.host + +import org.springframework.stereotype.Service +import org.w3c.dom.Document +import org.w3c.dom.Node +import me.mnlr.vripper.delegate.LoggerDelegate +import me.mnlr.vripper.download.ImageDownloadContext +import me.mnlr.vripper.exception.HostException +import me.mnlr.vripper.exception.XpathException +import me.mnlr.vripper.services.* + +@Service +class ImgboxHost( + private val xpathService: XpathService, + httpService: HTTPService, + htmlProcessorService: HtmlProcessorService, + dataTransaction: DataTransaction, + downloadSpeedService: DownloadSpeedService, +) : Host(httpService, htmlProcessorService, dataTransaction, downloadSpeedService) { + private val log by LoggerDelegate() + override val host: String + get() = Companion.host + + @Throws(HostException::class) + override fun resolve( + url: String, + document: Document, + context: ImageDownloadContext + ): Pair { + val imgNode: Node = try { + log.debug(String.format("Looking for xpath expression %s in %s", IMG_XPATH, url)) + xpathService.getAsNode(document, IMG_XPATH) + } catch (e: XpathException) { + throw HostException(e) + } ?: throw HostException( + String.format( + "Xpath '%s' cannot be found in '%s'", + IMG_XPATH, + url + ) + ) + return try { + log.debug(String.format("Resolving name and image url for %s", url)) + val imgTitle = imgNode.attributes.getNamedItem("title").textContent.trim { it <= ' ' } + val imgUrl = imgNode.attributes.getNamedItem("src").textContent.trim { it <= ' ' } + Pair(imgTitle, imgUrl) + } catch (e: Exception) { + throw HostException("Unexpected error occurred", e) + } + } + + companion object { + private const val host = "imgbox.com" + private const val lookup = "imgbox.com" + private const val IMG_XPATH = "//img[@id='img']" + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/host/ImxHost.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/host/ImxHost.kt new file mode 100644 index 00000000..73d0764d --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/host/ImxHost.kt @@ -0,0 +1,107 @@ +package me.mnlr.vripper.host + +import org.apache.http.NameValuePair +import org.apache.http.client.HttpClient +import org.apache.http.client.entity.UrlEncodedFormEntity +import org.apache.http.client.methods.CloseableHttpResponse +import org.apache.http.client.methods.HttpPost +import org.apache.http.message.BasicNameValuePair +import org.apache.http.util.EntityUtils +import org.springframework.stereotype.Service +import org.w3c.dom.Document +import org.w3c.dom.Node +import me.mnlr.vripper.delegate.LoggerDelegate +import me.mnlr.vripper.download.ImageDownloadContext +import me.mnlr.vripper.exception.HostException +import me.mnlr.vripper.exception.HtmlProcessorException +import me.mnlr.vripper.exception.XpathException +import me.mnlr.vripper.services.* +import java.io.IOException + +@Service +class ImxHost( + private val httpService: HTTPService, + private val htmlProcessorService: HtmlProcessorService, + private val xpathService: XpathService, + dataTransaction: DataTransaction, + downloadSpeedService: DownloadSpeedService, +) : Host(httpService, htmlProcessorService, dataTransaction, downloadSpeedService) { + private val log by LoggerDelegate() + override val host: String + get() = Companion.host + + @Throws(HostException::class) + override fun resolve( + url: String, + document: Document, + context: ImageDownloadContext + ): Pair { + var value: String? = null + try { + log.debug("Looking for xpath expression $CONTINUE_BUTTON_XPATH in $url") + val contDiv = xpathService.getAsNode(document, CONTINUE_BUTTON_XPATH) + ?: throw HostException("$CONTINUE_BUTTON_XPATH cannot be found") + val node = contDiv.attributes.getNamedItem("value") + if (node != null) { + value = node.textContent + } + } catch (e: XpathException) { + throw HostException(e) + } + if (value == null) { + throw HostException("Failed to obtain value attribute from continue input") + } + log.debug("Click button found for $url") + val client: HttpClient = httpService.client.build() + val httpPost: HttpPost = httpService.buildHttpPost(url, context.httpContext) + val params: MutableList = ArrayList() + params.add(BasicNameValuePair("imgContinue", value)) + try { + httpPost.entity = UrlEncodedFormEntity(params) + } catch (e: Exception) { + throw HostException(e) + } + log.debug("Requesting $httpPost") + val doc = try { + (client.execute( + httpPost, context.httpContext + ) as CloseableHttpResponse).use { response -> + log.debug("Cleaning response for $httpPost") + try { + htmlProcessorService.clean(response.entity.content) + } finally { + EntityUtils.consumeQuietly(response.entity) + } + } + } catch (e: IOException) { + throw HostException(e) + } catch (e: HtmlProcessorException) { + throw HostException(e) + } + val imgNode: Node = try { + log.debug("Looking for xpath expression $IMG_XPATH in $url") + xpathService.getAsNode(doc, IMG_XPATH) + } catch (e: XpathException) { + throw HostException(e) + } ?: throw HostException( + "Xpath $IMG_XPATH cannot be found in $url" + ) + return try { + log.debug("Resolving name and image url for $url") + val imgTitle = imgNode.attributes.getNamedItem("alt").textContent.trim { it <= ' ' } + val imgUrl = imgNode.attributes.getNamedItem("src").textContent.trim { it <= ' ' } + Pair( + imgTitle.ifEmpty { getDefaultImageName(imgUrl) }, imgUrl + ) + } catch (e: Exception) { + throw HostException("Unexpected error occurred", e) + } + } + + companion object { + private const val host = "imx.to" + private const val lookup = "imx.to" + private const val CONTINUE_BUTTON_XPATH = "//*[@name='imgContinue']" + private const val IMG_XPATH = "//img[@class='centred']" + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/host/PimpandhostHost.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/host/PimpandhostHost.kt new file mode 100644 index 00000000..d7ebc6f2 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/host/PimpandhostHost.kt @@ -0,0 +1,85 @@ +package me.mnlr.vripper.host + +import org.springframework.stereotype.Service +import org.w3c.dom.Document +import org.w3c.dom.Node +import me.mnlr.vripper.delegate.LoggerDelegate +import me.mnlr.vripper.download.ImageDownloadContext +import me.mnlr.vripper.exception.HostException +import me.mnlr.vripper.exception.XpathException +import me.mnlr.vripper.services.* +import java.net.URI +import java.net.URISyntaxException + +@Service +class PimpandhostHost( + private val xpathService: XpathService, + private val htmlProcessorService: HtmlProcessorService, + httpService: HTTPService, + dataTransaction: DataTransaction, + downloadSpeedService: DownloadSpeedService, +) : Host(httpService, htmlProcessorService, dataTransaction, downloadSpeedService) { + private val log by LoggerDelegate() + override val host: String + get() = Companion.host + + @Throws(HostException::class) + override fun resolve( + url: String, + document: Document, + context: ImageDownloadContext + ): Pair { + val newUrl: String + try { + newUrl = appendUri(url.replace("http://", "https://"), "size=original") + } catch (e: Exception) { + throw HostException(e) + } + val doc = fetch(newUrl, context) { + htmlProcessorService.clean(it.entity.content) + } + val imgNode: Node = try { + log.debug(String.format("Looking for xpath expression %s in %s", IMG_XPATH, newUrl)) + xpathService.getAsNode(doc, IMG_XPATH) + } catch (e: XpathException) { + throw HostException(e) + } ?: throw HostException( + String.format( + "Xpath '%s' cannot be found in '%s'", + IMG_XPATH, + url + ) + ) + return try { + log.debug(String.format("Resolving name and image url for %s", newUrl)) + val imgTitle = imgNode.attributes.getNamedItem("alt").textContent.trim { it <= ' ' } + val imgUrl = + "https:" + imgNode.attributes.getNamedItem("src").textContent.trim { it <= ' ' } + Pair( + imgTitle.ifEmpty { imgUrl.substring(imgUrl.lastIndexOf('/') + 1) }, imgUrl + ) + } catch (e: Exception) { + throw HostException("Unexpected error occurred", e) + } + } + + @Throws(URISyntaxException::class) + fun appendUri(uri: String, appendQuery: String): String { + val oldUri = URI(uri) + var newQuery = oldUri.query + if (newQuery == null) { + newQuery = appendQuery + } else { + newQuery += "&$appendQuery" + } + return URI( + oldUri.scheme, oldUri.authority, oldUri.path, newQuery, oldUri.fragment + ).toString() + } + + companion object { + private const val host = "pimpandhost.com" + private const val lookup = "pimpandhost.com" + private const val IMG_XPATH = "//img[contains(@class, 'original')]" + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/host/PixRouteHost.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/host/PixRouteHost.kt new file mode 100644 index 00000000..56c3d43f --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/host/PixRouteHost.kt @@ -0,0 +1,56 @@ +package me.mnlr.vripper.host + +import org.springframework.stereotype.Service +import org.w3c.dom.Document +import org.w3c.dom.Node +import me.mnlr.vripper.delegate.LoggerDelegate +import me.mnlr.vripper.download.ImageDownloadContext +import me.mnlr.vripper.exception.HostException +import me.mnlr.vripper.exception.XpathException +import me.mnlr.vripper.services.* + +@Service +class PixRouteHost( + private val xpathService: XpathService, + httpService: HTTPService, + htmlProcessorService: HtmlProcessorService, + dataTransaction: DataTransaction, + downloadSpeedService: DownloadSpeedService, +) : Host(httpService, htmlProcessorService, dataTransaction, downloadSpeedService) { + private val log by LoggerDelegate() + override val host: String + get() = Companion.host + + @Throws(HostException::class) + override fun resolve( + url: String, + document: Document, + context: ImageDownloadContext + ): Pair { + val imgNode: Node = try { + log.debug(String.format("Looking for xpath expression %s in %s", IMG_XPATH, url)) + xpathService.getAsNode(document, IMG_XPATH) + } catch (e: XpathException) { + throw HostException(e) + } ?: throw HostException( + String.format( + "Xpath '%s' cannot be found in '%s'", + IMG_XPATH, + url + ) + ) + return try { + log.debug(String.format("Resolving name and image url for %s", url)) + Pair(imgNode.attributes.getNamedItem("alt").textContent.trim { it <= ' ' }, + imgNode.attributes.getNamedItem("src").textContent.trim { it <= ' ' }) + } catch (e: Exception) { + throw HostException("Unexpected error occurred", e) + } + } + + companion object { + private const val host = "pixroute.com" + private const val lookup = "pixroute.com" + private const val IMG_XPATH = "//img[@id='imgpreview']" + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/host/PixhostHost.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/host/PixhostHost.kt new file mode 100644 index 00000000..d0cb9e56 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/host/PixhostHost.kt @@ -0,0 +1,57 @@ +package me.mnlr.vripper.host + +import org.springframework.stereotype.Service +import org.w3c.dom.Document +import org.w3c.dom.Node +import me.mnlr.vripper.delegate.LoggerDelegate +import me.mnlr.vripper.download.ImageDownloadContext +import me.mnlr.vripper.exception.HostException +import me.mnlr.vripper.exception.XpathException +import me.mnlr.vripper.services.* + +@Service +class PixhostHost( + private val xpathService: XpathService, + httpService: HTTPService, + htmlProcessorService: HtmlProcessorService, + dataTransaction: DataTransaction, + downloadSpeedService: DownloadSpeedService, +) : Host(httpService, htmlProcessorService, dataTransaction, downloadSpeedService) { + private val log by LoggerDelegate() + override val host: String + get() = Companion.host + + @Throws(HostException::class) + override fun resolve( + url: String, + document: Document, + context: ImageDownloadContext + ): Pair { + val imgNode: Node = try { + log.debug(String.format("Looking for xpath expression %s in %s", IMG_XPATH, url)) + xpathService.getAsNode(document, IMG_XPATH) + } catch (e: XpathException) { + throw HostException(e) + } ?: throw HostException( + String.format( + "Xpath '%s' cannot be found in '%s'", + IMG_XPATH, + url + ) + ) + return try { + log.debug(String.format("Resolving name and image url for %s", url)) + val imgTitle = imgNode.attributes.getNamedItem("alt").textContent.trim { it <= ' ' } + val imgUrl = imgNode.attributes.getNamedItem("src").textContent.trim { it <= ' ' } + Pair(imgTitle.substring(imgTitle.indexOf('_') + 1), imgUrl) + } catch (e: Exception) { + throw HostException("Unexpected error occurred", e) + } + } + + companion object { + private const val host = "pixhost.to" + private const val lookup = "pixhost.to" + private const val IMG_XPATH = "//img[@id='image']" + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/host/PixxxelsHost.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/host/PixxxelsHost.kt new file mode 100644 index 00000000..92d6154a --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/host/PixxxelsHost.kt @@ -0,0 +1,71 @@ +package me.mnlr.vripper.host + +import org.springframework.stereotype.Service +import org.w3c.dom.Document +import me.mnlr.vripper.delegate.LoggerDelegate +import me.mnlr.vripper.download.ImageDownloadContext +import me.mnlr.vripper.exception.HostException +import me.mnlr.vripper.exception.XpathException +import me.mnlr.vripper.services.* + +@Service +class PixxxelsHost( + private val xpathService: XpathService, + httpService: HTTPService, + htmlProcessorService: HtmlProcessorService, + dataTransaction: DataTransaction, + downloadSpeedService: DownloadSpeedService, +) : Host(httpService, htmlProcessorService, dataTransaction, downloadSpeedService) { + private val log by LoggerDelegate() + override val host: String + get() = Companion.host + + @Throws(HostException::class) + override fun resolve( + url: String, + document: Document, + context: ImageDownloadContext + ): Pair { + val imgNode = try { + log.debug(String.format("Looking for xpath expression %s in %s", IMG_XPATH, url)) + xpathService.getAsNode(document, IMG_XPATH) + } catch (e: XpathException) { + throw HostException(e) + } ?: throw HostException( + String.format( + "Xpath '%s' cannot be found in '%s'", + IMG_XPATH, + url + ) + ) + val titleNode = try { + log.debug(String.format("Looking for xpath expression %s in %s", TITLE_XPATH, url)) + xpathService.getAsNode(document, TITLE_XPATH) + } catch (e: XpathException) { + throw HostException(e) + } ?: throw HostException( + String.format( + "Xpath '%s' cannot be found in '%s'", + TITLE_XPATH, + url + ) + ) + return try { + log.debug(String.format("Resolving name and image url for %s", url)) + val imgTitle = titleNode.textContent.trim { it <= ' ' } + val imgUrl = imgNode.attributes.getNamedItem("href").textContent.trim { it <= ' ' } + Pair( + imgTitle.ifEmpty { imgUrl.substring(imgUrl.lastIndexOf('/') + 1) }, imgUrl + ) + } catch (e: Exception) { + throw HostException("Unexpected error occurred", e) + } + } + + companion object { + private const val host = "pixxxels.cc" + private const val lookup = "pixxxels.cc" + private const val IMG_XPATH = "//*[@id='download']" + private const val TITLE_XPATH = "//*[contains(@class,'imagename')]" + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/host/PostImgHost.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/host/PostImgHost.kt new file mode 100644 index 00000000..5752ce3e --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/host/PostImgHost.kt @@ -0,0 +1,72 @@ +package me.mnlr.vripper.host + +import org.springframework.stereotype.Service +import org.w3c.dom.Document +import org.w3c.dom.Node +import me.mnlr.vripper.delegate.LoggerDelegate +import me.mnlr.vripper.download.ImageDownloadContext +import me.mnlr.vripper.exception.HostException +import me.mnlr.vripper.exception.XpathException +import me.mnlr.vripper.services.* +import java.util.* + +@Service +class PostImgHost( + private val xpathService: XpathService, + httpService: HTTPService, + htmlProcessorService: HtmlProcessorService, + dataTransaction: DataTransaction, + downloadSpeedService: DownloadSpeedService, +) : Host(httpService, htmlProcessorService, dataTransaction, downloadSpeedService) { + private val log by LoggerDelegate() + override val host: String + get() = Companion.host + + @Throws(HostException::class) + override fun resolve( + url: String, + document: Document, + context: ImageDownloadContext + ): Pair { + val titleNode = try { + log.debug(String.format("Looking for xpath expression %s in %s", TITLE_XPATH, url)) + xpathService.getAsNode(document, TITLE_XPATH) + } catch (e: XpathException) { + throw HostException(e) + } ?: throw HostException( + String.format( + "Xpath '%s' cannot be found in '%s'", + TITLE_XPATH, + url + ) + ) + val urlNode = try { + log.debug(String.format("Looking for xpath expression %s in %s", IMG_XPATH, url)) + xpathService.getAsNode(document, IMG_XPATH) + } catch (e: XpathException) { + throw HostException(e) + } ?: throw HostException( + String.format( + "Xpath '%s' cannot be found in '%s'", + IMG_XPATH, + url + ) + ) + return try { + log.debug(String.format("Resolving name and image url for %s", url)) + val imgTitle = Optional.ofNullable(titleNode) + .map { node: Node -> node.textContent.trim { it <= ' ' } } + .orElseGet { getDefaultImageName(url) } + Pair(imgTitle, urlNode.attributes.getNamedItem("href").textContent.trim { it <= ' ' }) + } catch (e: Exception) { + throw HostException("Unexpected error occurred", e) + } + } + + companion object { + private const val host = "postimg.cc" + private const val lookup = "postimg.cc" + private const val TITLE_XPATH = "//span[contains(@class,'imagename')]" + private const val IMG_XPATH = "//a[@id='download']" + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/host/TurboImageHost.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/host/TurboImageHost.kt new file mode 100644 index 00000000..44757b76 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/host/TurboImageHost.kt @@ -0,0 +1,59 @@ +package me.mnlr.vripper.host + +import org.springframework.stereotype.Service +import org.w3c.dom.Document +import org.w3c.dom.Node +import me.mnlr.vripper.delegate.LoggerDelegate +import me.mnlr.vripper.download.ImageDownloadContext +import me.mnlr.vripper.exception.HostException +import me.mnlr.vripper.exception.XpathException +import me.mnlr.vripper.services.* + +@Service +class TurboImageHost( + private val xpathService: XpathService, + httpService: HTTPService, + htmlProcessorService: HtmlProcessorService, + dataTransaction: DataTransaction, + downloadSpeedService: DownloadSpeedService, +) : Host(httpService, htmlProcessorService, dataTransaction, downloadSpeedService) { + private val log by LoggerDelegate() + override val host: String + get() = Companion.host + + @Throws(HostException::class) + override fun resolve( + url: String, + document: Document, + context: ImageDownloadContext + ): Pair { + var title: String? + title = try { + log.debug(String.format("Looking for xpath expression %s in %s", TITLE_XPATH, url)) + val titleNode: Node? = xpathService.getAsNode(document, TITLE_XPATH) + log.debug(String.format("Resolving name for %s", url)) + titleNode?.textContent?.trim { it <= ' ' } + } catch (e: XpathException) { + throw HostException(e) + } + if (title.isNullOrEmpty()) { + title = getDefaultImageName(url) + } + val urlNode: Node = xpathService.getAsNode(document, IMG_XPATH) + ?: throw HostException( + String.format( + "Xpath '%s' cannot be found in '%s'", + IMG_XPATH, + url + ) + ) + return Pair(title, urlNode.attributes.getNamedItem("src").textContent.trim { it <= ' ' }) + } + + companion object { + private const val host = "turboimagehost.com" + private const val lookup = "turboimagehost.com" + private const val TITLE_XPATH = "//div[contains(@class,'titleFullS')]/h1" + private const val IMG_XPATH = "//img[@id='imageid']" + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/host/ViprImHost.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/host/ViprImHost.kt new file mode 100644 index 00000000..12e6d588 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/host/ViprImHost.kt @@ -0,0 +1,58 @@ +package me.mnlr.vripper.host + +import org.springframework.stereotype.Service +import org.w3c.dom.Document +import org.w3c.dom.Node +import me.mnlr.vripper.delegate.LoggerDelegate +import me.mnlr.vripper.download.ImageDownloadContext +import me.mnlr.vripper.exception.HostException +import me.mnlr.vripper.exception.XpathException +import me.mnlr.vripper.services.* +import java.util.* + +@Service +class ViprImHost( + private val xpathService: XpathService, + httpService: HTTPService, + htmlProcessorService: HtmlProcessorService, + dataTransaction: DataTransaction, + downloadSpeedService: DownloadSpeedService, +) : Host(httpService, htmlProcessorService, dataTransaction, downloadSpeedService) { + private val log by LoggerDelegate() + override val host: String = "vipr.im" + + @Throws(HostException::class) + override fun resolve( + url: String, + document: Document, + context: ImageDownloadContext + ): Pair { + val imgNode: Node = try { + log.debug(String.format("Looking for xpath expression %s in %s", IMG_XPATH, url)) + xpathService.getAsNode(document, IMG_XPATH) + } catch (e: XpathException) { + throw HostException(e) + } ?: throw HostException( + String.format( + "Xpath '%s' cannot be found in '%s'", + IMG_XPATH, + url + ) + ) + return try { + log.debug(String.format("Resolving name and image url for %s", url)) + val imgTitle = + Optional.ofNullable(imgNode.attributes.getNamedItem("alt")) + .map { obj: Node -> obj.textContent } + .map { obj: String -> obj.trim() }.orElse(null) + val imgUrl = imgNode.attributes.getNamedItem("src").textContent.trim() + Pair(imgTitle!!, imgUrl) + } catch (e: Exception) { + throw HostException("Unexpected error occurred", e) + } + } + + companion object { + private const val IMG_XPATH = "//img[contains(@class, 'img')]" + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/model/DownloadSpeed.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/model/DownloadSpeed.kt new file mode 100644 index 00000000..30ac3398 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/model/DownloadSpeed.kt @@ -0,0 +1,19 @@ +package me.mnlr.vripper.model + +data class DownloadSpeed(val speed: String) { + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as DownloadSpeed + + if (speed != other.speed) return false + + return true + } + + override fun hashCode(): Int { + return speed.hashCode() + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/model/GlobalState.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/model/GlobalState.kt new file mode 100644 index 00000000..9941459c --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/model/GlobalState.kt @@ -0,0 +1,33 @@ +package me.mnlr.vripper.model + +data class GlobalState( + val running: Int, + val remaining: Int, + val error: Int, + val loggedUser: String, + val downloadSpeed: String +) { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as GlobalState + + if (running != other.running) return false + if (remaining != other.remaining) return false + if (error != other.error) return false + if (loggedUser != other.loggedUser) return false + if (downloadSpeed != other.downloadSpeed) return false + + return true + } + + override fun hashCode(): Int { + var result = running + result = 31 * result + remaining + result = 31 * result + error + result = 31 * result + loggedUser.hashCode() + result = 31 * result + downloadSpeed.hashCode() + return result + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/model/ImageItem.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/model/ImageItem.kt new file mode 100644 index 00000000..3bcca2ea --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/model/ImageItem.kt @@ -0,0 +1,5 @@ +package me.mnlr.vripper.model + +import me.mnlr.vripper.host.Host + +data class ImageItem(val mainLink: String, val thumbLink: String, val host: Host) \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/model/LoggedUser.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/model/LoggedUser.kt new file mode 100644 index 00000000..fa824e60 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/model/LoggedUser.kt @@ -0,0 +1,3 @@ +package me.mnlr.vripper.model + +data class LoggedUser(val user: String) \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/model/PostItem.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/model/PostItem.kt new file mode 100644 index 00000000..86353ca2 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/model/PostItem.kt @@ -0,0 +1,17 @@ +package me.mnlr.vripper.model + +import me.mnlr.vripper.host.Host + +data class PostItem( + val threadId: String, + val threadTitle: String, + val postId: String, + val number: Int, + val title: String, + val imageCount: Int, + val url: String, + val hosts: Map, + val securityToken: String, + val forum: String, + val imageItemList: List +) \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/model/Settings.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/model/Settings.kt new file mode 100644 index 00000000..244477c4 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/model/Settings.kt @@ -0,0 +1,35 @@ +package me.mnlr.vripper.model + +class Settings { + var desktopClipboard: Boolean = false + var maxEventLog: Int = 1_000 + var connectionSettings: ConnectionSettings = ConnectionSettings() + var downloadSettings: DownloadSettings = DownloadSettings() + var viperSettings: ViperSettings = ViperSettings() +} + +data class ViperSettings( + var login: Boolean = false, + var username: String = "", + var password: String = "", + var thanks: Boolean = false, + var host: String = "https://vipergirls.to", +) + +data class DownloadSettings( + var downloadPath: String = System.getProperty("user.home"), + var autoStart: Boolean = true, + var autoQueueThreshold: Int = 1, + var forceOrder: Boolean = false, + var forumSubfolder: Boolean = false, + var threadSubLocation: Boolean = false, + var clearCompleted: Boolean = false, + var appendPostId: Boolean = false +) + +data class ConnectionSettings( + var maxThreads: Int = 4, + var maxTotalThreads: Int = 0, + var timeout: Int = 30, + var maxAttempts: Int = 3, +) \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/model/ThreadInput.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/model/ThreadInput.kt new file mode 100644 index 00000000..af23af2c --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/model/ThreadInput.kt @@ -0,0 +1,3 @@ +package me.mnlr.vripper.model + +data class ThreadInput(val link: String, val threadId: String) \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/model/ThreadItem.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/model/ThreadItem.kt new file mode 100644 index 00000000..014e68e8 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/model/ThreadItem.kt @@ -0,0 +1,9 @@ +package me.mnlr.vripper.model + +data class ThreadItem( + val threadId: String, + val title: String, + val securityToken: String, + val forum: String, + val postItemList: List +) diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/parser/ThreadLookupAPIParser.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/parser/ThreadLookupAPIParser.kt new file mode 100644 index 00000000..54bb2626 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/parser/ThreadLookupAPIParser.kt @@ -0,0 +1,79 @@ +package me.mnlr.vripper.parser + +import net.jodah.failsafe.Failsafe +import net.jodah.failsafe.function.CheckedSupplier +import org.apache.http.client.HttpClient +import org.apache.http.client.methods.CloseableHttpResponse +import org.apache.http.client.methods.HttpGet +import org.apache.http.client.protocol.HttpClientContext +import org.apache.http.client.utils.URIBuilder +import org.apache.http.util.EntityUtils +import me.mnlr.vripper.SpringContext +import me.mnlr.vripper.delegate.LoggerDelegate +import me.mnlr.vripper.exception.DownloadException +import me.mnlr.vripper.exception.PostParseException +import me.mnlr.vripper.model.ThreadItem +import me.mnlr.vripper.services.HTTPService +import me.mnlr.vripper.services.RetryPolicyService +import me.mnlr.vripper.services.SettingsService +import me.mnlr.vripper.services.VGAuthService +import java.io.BufferedInputStream +import java.net.URISyntaxException +import java.util.* +import javax.xml.parsers.SAXParserFactory + +class ThreadLookupAPIParser(private val threadId: String) { + private val log by LoggerDelegate() + private val cm: HTTPService = SpringContext.getBean(HTTPService::class.java) + private val retryPolicyService: RetryPolicyService = + SpringContext.getBean(RetryPolicyService::class.java) + private val vgAuthService: VGAuthService = SpringContext.getBean(VGAuthService::class.java) + private val settingsService: SettingsService = + SpringContext.getBean(SettingsService::class.java) + + @Throws(PostParseException::class) + fun parse(): ThreadItem { + log.debug("Parsing thread $threadId") + val httpGet: HttpGet = try { + val uriBuilder = URIBuilder(settingsService.settings.viperSettings.host + "/vr.php") + uriBuilder.setParameter("t", threadId) + cm.buildHttpGet(uriBuilder.build(), HttpClientContext.create()) + } catch (e: URISyntaxException) { + throw PostParseException(e) + } + val threadLookupAPIResponseHandler = ThreadLookupAPIResponseHandler() + log.debug("Requesting $httpGet") + return try { + Failsafe.with(retryPolicyService.buildGenericRetryPolicy()).onFailure { + log.error( + "parsing failed for thread $threadId", + it.failure + ) + }.get(CheckedSupplier { + val connection: HttpClient = cm.client.build() + (connection.execute( + httpGet, vgAuthService.context + ) as CloseableHttpResponse).use { response -> + try { + if (response.statusLine.statusCode / 100 != 2) { + throw DownloadException("Unexpected response code '${response.statusLine.statusCode}' for $httpGet") + } + factory.newSAXParser().parse( + BufferedInputStream(response.entity.content), + threadLookupAPIResponseHandler + ) + threadLookupAPIResponseHandler.result + } finally { + EntityUtils.consumeQuietly(response.entity) + } + } + }) + } catch (e: Exception) { + throw PostParseException(e) + } + } + + companion object { + private val factory = SAXParserFactory.newInstance() + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/parser/ThreadLookupAPIResponseHandler.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/parser/ThreadLookupAPIResponseHandler.kt new file mode 100644 index 00000000..2625b4d1 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/parser/ThreadLookupAPIResponseHandler.kt @@ -0,0 +1,104 @@ +package me.mnlr.vripper.parser + +import org.xml.sax.Attributes +import org.xml.sax.helpers.DefaultHandler +import me.mnlr.vripper.SpringContext +import me.mnlr.vripper.delegate.LoggerDelegate +import me.mnlr.vripper.host.Host +import me.mnlr.vripper.model.ImageItem +import me.mnlr.vripper.model.PostItem +import me.mnlr.vripper.model.ThreadItem +import me.mnlr.vripper.services.SettingsService + +class ThreadLookupAPIResponseHandler : DefaultHandler() { + private val log by LoggerDelegate() + private val supportedHosts: Collection = + SpringContext.getBeansOfType(Host::class.java).values + private val settingsService: SettingsService = + SpringContext.getBean(SettingsService::class.java) + private var error: String = "" + private val hostMap: MutableMap = mutableMapOf() + private val postItemList: MutableList = mutableListOf() + private val imageItemList: MutableList = mutableListOf() + private lateinit var threadItem: ThreadItem + private lateinit var threadId: String + private lateinit var threadTitle: String + private lateinit var forum: String + private lateinit var securityToken: String + private lateinit var postId: String + private lateinit var postTitle: String + private var postCounter: Int = 0 + + val result: ThreadItem + get() = threadItem + + override fun startDocument() {} + override fun startElement( + uri: String, localName: String, qName: String, attributes: Attributes + ) { + when (qName.lowercase()) { + "error" -> error = attributes.getValue("details") + "thread" -> { + threadId = attributes.getValue("id")?.trim() ?: "" + threadTitle = attributes.getValue("title")?.trim() ?: "" + } + + "forum" -> forum = attributes.getValue("title")?.trim() ?: "" + "user" -> securityToken = attributes.getValue("hash")?.trim() ?: "" + "post" -> { + postId = attributes.getValue("id")?.trim() ?: "" + postCounter = attributes.getValue("number")?.trim()?.toInt() ?: 0 + val title = attributes.getValue("title")?.trim() ?: "" + postTitle = title.ifBlank { threadTitle } + } + + "image" -> { + val mainLink = attributes.getValue("main_url")?.trim() ?: "" + val thumbLink = attributes.getValue("thumb_url")?.trim() ?: "" + val type = attributes.getValue("type")?.trim() ?: "" + if(type == "linked") { + try { + val host = supportedHosts.first { + it.isSupported(mainLink) + }.let { host -> + hostMap.computeIfAbsent(host) { 0 } + hostMap[host] = hostMap[host]!! + 1 + host + } + imageItemList.add(ImageItem(mainLink, thumbLink, host)) + } catch (e: Exception) { + log.warn("Unsupported link: $mainLink") + } + } + } + } + } + + override fun endElement(uri: String, localName: String, qName: String) { + if ("post".equals(qName, true)) { + if (imageItemList.isNotEmpty()) { + postItemList.add( + PostItem( + threadId, + threadTitle, + postId, + postCounter, + postTitle, + imageItemList.size, + "${settingsService.settings.viperSettings.host}/threads/?p=$postId&viewfull=1#post$postId", + hostMap.toMap(), + securityToken, + forum, + imageItemList.toList() + ) + ) + } + imageItemList.clear() + hostMap.clear() + } + } + + override fun endDocument() { + threadItem = ThreadItem(threadId, threadTitle, securityToken, forum, postItemList.toList()) + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/repositories/ImageRepository.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/repositories/ImageRepository.kt new file mode 100644 index 00000000..1c3a7c03 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/repositories/ImageRepository.kt @@ -0,0 +1,16 @@ +package me.mnlr.vripper.repositories + +import me.mnlr.vripper.entities.ImageDownloadState +import java.util.* + +interface ImageRepository { + fun save(imageDownloadState: ImageDownloadState): ImageDownloadState + fun deleteAllByPostId(postId: String) + fun findByPostId(postId: String): List + fun countError(): Int + fun findByPostIdAndIsNotCompleted(postId: String): List + fun stopByPostIdAndIsNotCompleted(postId: String): Int + fun findByPostIdAndIsError(postId: String): List + fun findById(id: Long): Optional + fun update(imageDownloadState: ImageDownloadState) +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/repositories/LogEventRepository.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/repositories/LogEventRepository.kt new file mode 100644 index 00000000..24db701f --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/repositories/LogEventRepository.kt @@ -0,0 +1,13 @@ +package me.mnlr.vripper.repositories + +import me.mnlr.vripper.entities.LogEvent +import java.util.* + +interface LogEventRepository { + fun save(logEvent: LogEvent): LogEvent + fun update(logEvent: LogEvent): LogEvent + fun findAll(): List + fun findById(id: Long): Optional + fun delete(id: Long) + fun deleteAll() +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/repositories/MetadataRepository.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/repositories/MetadataRepository.kt new file mode 100644 index 00000000..e7134f8d --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/repositories/MetadataRepository.kt @@ -0,0 +1,10 @@ +package me.mnlr.vripper.repositories + +import me.mnlr.vripper.entities.Metadata +import java.util.* + +interface MetadataRepository { + fun save(metadata: Metadata): Metadata + fun findByPostId(postId: String): Optional + fun deleteByPostId(postId: String): Int +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/repositories/PostDownloadStateRepository.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/repositories/PostDownloadStateRepository.kt new file mode 100644 index 00000000..2ca3688f --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/repositories/PostDownloadStateRepository.kt @@ -0,0 +1,16 @@ +package me.mnlr.vripper.repositories + +import me.mnlr.vripper.entities.PostDownloadState +import java.util.* + +interface PostDownloadStateRepository { + fun save(postDownloadState: PostDownloadState): PostDownloadState + fun findByPostId(postId: String): Optional + fun findById(id: Long): Optional + fun findCompleted(): List + fun findAll(): List + fun existByPostId(postId: String): Boolean + fun setDownloadingToStopped(): Int + fun deleteByPostId(postId: String): Int + fun update(postDownloadState: PostDownloadState) +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/repositories/ThreadRepository.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/repositories/ThreadRepository.kt new file mode 100644 index 00000000..03efb3d1 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/repositories/ThreadRepository.kt @@ -0,0 +1,13 @@ +package me.mnlr.vripper.repositories + +import me.mnlr.vripper.entities.Thread +import java.util.* + +interface ThreadRepository { + fun save(thread: Thread): Thread + fun findByThreadId(threadId: String): Optional + fun findAll(): List + fun findById(id: Long): Optional + fun deleteByThreadId(threadId: String): Int + fun deleteAll() +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/repositories/impl/ImageRepositoryImpl.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/repositories/impl/ImageRepositoryImpl.kt new file mode 100644 index 00000000..8acdeb0d --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/repositories/impl/ImageRepositoryImpl.kt @@ -0,0 +1,125 @@ +package me.mnlr.vripper.repositories.impl + +import org.springframework.jdbc.core.JdbcTemplate +import org.springframework.jdbc.core.RowMapper +import org.springframework.stereotype.Service +import me.mnlr.vripper.SpringContext +import me.mnlr.vripper.entities.ImageDownloadState +import me.mnlr.vripper.entities.domain.Status +import me.mnlr.vripper.event.Event +import me.mnlr.vripper.event.EventBus +import me.mnlr.vripper.host.Host +import me.mnlr.vripper.repositories.ImageRepository +import java.sql.ResultSet +import java.sql.SQLException +import java.util.* + +@Service +class ImageRepositoryImpl( + private val jdbcTemplate: JdbcTemplate, private val eventBus: EventBus +) : ImageRepository { + @Synchronized + private fun nextId(): Long { + return jdbcTemplate.queryForObject("CALL NEXT VALUE FOR SEQ_IMAGE", Long::class.java)!! + } + + override fun save(imageDownloadState: ImageDownloadState): ImageDownloadState { + val id = nextId() + jdbcTemplate.update( + "INSERT INTO IMAGE (ID, CURRENT, HOST, INDEX, POST_ID, STATUS, TOTAL, URL, POST_ID_REF) VALUES (?,?,?,?,?,?,?,?,?)", + id, + imageDownloadState.current, + imageDownloadState.host.host, + imageDownloadState.index, + imageDownloadState.postId, + imageDownloadState.status.name, + imageDownloadState.total, + imageDownloadState.url, + imageDownloadState.postIdRef + ) + imageDownloadState.id = id + eventBus.publishEvent(Event(Event.Kind.IMAGE_UPDATE, id)) + return imageDownloadState + } + + override fun deleteAllByPostId(postId: String) { + jdbcTemplate.update("DELETE FROM IMAGE WHERE POST_ID = ?", postId) + } + + override fun findByPostId(postId: String): List { + return jdbcTemplate.query( + "SELECT * FROM IMAGE WHERE POST_ID = ?", ImageRowMapper(), postId + ) + } + + override fun countError(): Int { + return jdbcTemplate.queryForObject( + "SELECT COUNT(*) FROM IMAGE AS image WHERE image.STATUS = ?", Int::class.java, Status.ERROR.name + ) + } + + override fun findByPostIdAndIsNotCompleted(postId: String): List { + return jdbcTemplate.query( + "SELECT * FROM IMAGE AS image WHERE image.POST_ID = ? AND image.STATUS <> ?", + ImageRowMapper(), + postId, + Status.FINISHED.name + ) + } + + override fun stopByPostIdAndIsNotCompleted(postId: String): Int { + return jdbcTemplate.update( + "UPDATE IMAGE AS image SET image.STATUS = ? WHERE image.POST_ID = ? AND image.STATUS <> ?", + Status.STOPPED.name, + postId, + Status.FINISHED.name + ) + } + + override fun findByPostIdAndIsError(postId: String): List { + return jdbcTemplate.query( + "SELECT * FROM IMAGE AS image WHERE image.POST_ID = ? AND image.STATUS = ?", + ImageRowMapper(), + postId, + Status.ERROR.name + ) + } + + override fun findById(id: Long): Optional { + val images = jdbcTemplate.query( + "SELECT * FROM IMAGE AS image WHERE image.ID = ?", ImageRowMapper(), id + ) + return if (images.isEmpty()) { + Optional.empty() + } else { + Optional.of(images[0]) + } + } + + override fun update(imageDownloadState: ImageDownloadState) { + jdbcTemplate.update( + "UPDATE IMAGE AS image SET image.STATUS = ?, image.CURRENT = ?, image.TOTAL = ? WHERE image.ID = ?", + imageDownloadState.status.name, + imageDownloadState.current, + imageDownloadState.total, + imageDownloadState.id + ) + eventBus.publishEvent(Event(Event.Kind.IMAGE_UPDATE, imageDownloadState.id)) + } +} + +internal class ImageRowMapper : RowMapper { + @Throws(SQLException::class) + override fun mapRow(rs: ResultSet, rowNum: Int): ImageDownloadState { + val id = rs.getLong("ID") + val postId = rs.getString("POST_ID") + val url = rs.getString("URL") + val host = SpringContext.getBeansOfType(Host::class.java).values.filter { it.host == rs.getString("HOST") }[0] + val index = rs.getInt("INDEX") + val current = rs.getLong("CURRENT") + val total = rs.getLong("TOTAL") + val status = Status.valueOf(rs.getString("STATUS")) + val postIdRef = rs.getLong("POST_ID_REF") + return ImageDownloadState(id, postId, url, host, index, postIdRef, total, current, status) + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/repositories/impl/LogEventRepositoryImpl.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/repositories/impl/LogEventRepositoryImpl.kt new file mode 100644 index 00000000..a6193993 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/repositories/impl/LogEventRepositoryImpl.kt @@ -0,0 +1,107 @@ +package me.mnlr.vripper.repositories.impl + +import org.springframework.jdbc.core.JdbcTemplate +import org.springframework.jdbc.core.RowMapper +import org.springframework.stereotype.Service +import me.mnlr.vripper.delegate.LoggerDelegate +import me.mnlr.vripper.entities.LogEvent +import me.mnlr.vripper.event.Event +import me.mnlr.vripper.event.EventBus +import me.mnlr.vripper.repositories.LogEventRepository +import me.mnlr.vripper.services.SettingsService +import java.sql.ResultSet +import java.sql.SQLException +import java.time.LocalDateTime +import java.time.format.DateTimeFormatter +import java.util.* + +@Service +class LogEventRepositoryImpl( + private val jdbcTemplate: JdbcTemplate, + private val settingsService: SettingsService, + private val eventBus: EventBus +) : LogEventRepository { + + private val log by LoggerDelegate() + + @Synchronized + private fun nextId(): Long { + return jdbcTemplate.queryForObject("CALL NEXT VALUE FOR SEQ_EVENT", Long::class.java)!! + } + + @Synchronized + override fun save(logEvent: LogEvent): LogEvent { + val maxRecords = settingsService.settings.maxEventLog - 1 + val count = jdbcTemplate.queryForObject("SELECT COUNT(*) FROM EVENT", Long::class.java) ?: 0 + if (count > maxRecords) { + val idList = jdbcTemplate.queryForList( + "SELECT ID FROM EVENT ORDER BY TIME ASC LIMIT ?", + Long::class.java, + count - maxRecords + ) + idList.forEach { delete(it) } + } + val id = nextId() + jdbcTemplate.update( + "INSERT INTO EVENT (ID, TYPE, STATUS, TIME, MESSAGE) VALUES (?,?,?,?,?)", + id, + logEvent.type.name, + logEvent.status.name, + logEvent.time.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME), + logEvent.message + ) + eventBus.publishEvent(Event(Event.Kind.LOG_EVENT_UPDATE, id)) + return logEvent.copy(id = id) + } + + override fun update(logEvent: LogEvent): LogEvent { + if (logEvent.id == null) { + log.warn("Cannot update entity with null id") + return logEvent + } + jdbcTemplate.update( + "UPDATE EVENT SET STATUS = ?, MESSAGE = ? WHERE ID = ?", + logEvent.status.name, + logEvent.message, + logEvent.id + ) + eventBus.publishEvent(Event(Event.Kind.LOG_EVENT_UPDATE, logEvent.id)) + return logEvent + } + + override fun findById(id: Long): Optional { + val logEvents = + jdbcTemplate.query("SELECT * FROM EVENT WHERE ID = ?", LogEventRowMapper(), id) + return if (logEvents.isEmpty()) { + Optional.empty() + } else { + Optional.of(logEvents[0]) + } + } + + override fun findAll(): List { + return jdbcTemplate.query("SELECT * FROM EVENT", LogEventRowMapper()) + } + + override fun delete(id: Long) { + jdbcTemplate.update("DELETE FROM EVENT WHERE ID = ?", id) + eventBus.publishEvent(Event(Event.Kind.LOG_EVENT_REMOVE, id)) + } + + override fun deleteAll() { + jdbcTemplate.update("DELETE FROM EVENT") + } +} + +internal class LogEventRowMapper : RowMapper { + @Throws(SQLException::class) + override fun mapRow(rs: ResultSet, rowNum: Int): LogEvent { + val id = rs.getLong("ID") + val type = LogEvent.Type.valueOf(rs.getString("TYPE")) + val status = LogEvent.Status.valueOf(rs.getString("STATUS")) + val time = + LocalDateTime.parse(rs.getString("TIME"), DateTimeFormatter.ISO_LOCAL_DATE_TIME) + val message = rs.getString("MESSAGE") + return LogEvent(id, type, status, time, message) + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/repositories/impl/MetadataRepositoryImpl.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/repositories/impl/MetadataRepositoryImpl.kt new file mode 100644 index 00000000..a0ea1f42 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/repositories/impl/MetadataRepositoryImpl.kt @@ -0,0 +1,65 @@ +package me.mnlr.vripper.repositories.impl + +import org.springframework.jdbc.core.JdbcTemplate +import org.springframework.jdbc.core.RowMapper +import org.springframework.stereotype.Service +import me.mnlr.vripper.entities.Metadata +import me.mnlr.vripper.event.Event +import me.mnlr.vripper.event.EventBus +import me.mnlr.vripper.repositories.* +import java.sql.ResultSet +import java.sql.SQLException +import java.util.* + +@Service +class MetadataRepositoryImpl( + private val jdbcTemplate: JdbcTemplate, + private val eventBus: EventBus +) : MetadataRepository { + override fun save(metadata: Metadata): Metadata { + jdbcTemplate.update( + "INSERT INTO METADATA (POST_ID_REF, POST_ID, POSTED_BY, RESOLVED_NAMES) VALUES (?,?,?,?)", + metadata.postIdRef, + metadata.postId, + metadata.postedBy, + java.lang.String.join("%sep%", metadata.resolvedNames) + ) + eventBus.publishEvent(Event(Event.Kind.METADATA_UPDATE, metadata.postIdRef)) + return metadata + } + + override fun findByPostId(postId: String): Optional { + val metadata = jdbcTemplate.query( + "SELECT metadata.* FROM METADATA AS metadata WHERE metadata.POST_ID = ?", + MetadataRowMapper(), + postId + ) + return if (metadata.isEmpty()) { + Optional.empty() + } else { + Optional.of(metadata[0]) + } + } + + override fun deleteByPostId(postId: String): Int { + return jdbcTemplate.update( + "DELETE FROM METADATA AS metadata WHERE metadata.POST_ID = ?", postId + ) + } +} + +internal class MetadataRowMapper : RowMapper { + @Throws(SQLException::class) + override fun mapRow(rs: ResultSet, rowNum: Int): Metadata { + val metadata = Metadata() + metadata.postIdRef = rs.getLong("POST_ID_REF") + metadata.postId = rs.getString("POST_ID") + metadata.postedBy = rs.getString("POSTED_BY") + val resolvedNames = rs.getString("RESOLVED_NAMES") + if (resolvedNames != null && resolvedNames.isNotBlank()) { + metadata.resolvedNames = + resolvedNames.split("%sep%").dropLastWhile { it.isEmpty() } + } + return metadata + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/repositories/impl/PostDownloadStateRepositoryImpl.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/repositories/impl/PostDownloadStateRepositoryImpl.kt new file mode 100644 index 00000000..44b36050 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/repositories/impl/PostDownloadStateRepositoryImpl.kt @@ -0,0 +1,163 @@ +package me.mnlr.vripper.repositories.impl + +import org.springframework.jdbc.core.JdbcTemplate +import org.springframework.jdbc.core.RowMapper +import org.springframework.stereotype.Service +import me.mnlr.vripper.entities.PostDownloadState +import me.mnlr.vripper.entities.domain.Status +import me.mnlr.vripper.event.Event +import me.mnlr.vripper.event.EventBus +import me.mnlr.vripper.repositories.PostDownloadStateRepository +import java.sql.ResultSet +import java.sql.SQLException +import java.sql.Timestamp +import java.util.* + +@Service +class PostDownloadStateRepositoryImpl( + private val jdbcTemplate: JdbcTemplate, private val eventBus: EventBus +) : PostDownloadStateRepository { + @Synchronized + private fun nextId(): Long { + return jdbcTemplate.queryForObject("CALL NEXT VALUE FOR SEQ_POST", Long::class.java)!! + } + + override fun save(postDownloadState: PostDownloadState): PostDownloadState { + val id = nextId() + jdbcTemplate.update( + "INSERT INTO POST (ID, DONE, HOSTS, POST_FOLDER_NAME, POST_ID, STATUS, THREAD_ID, POST_TITLE, THREAD_TITLE, FORUM, TOTAL, URL, TOKEN, ADDED_ON, RANK) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", + id, + postDownloadState.done, + java.lang.String.join(";", postDownloadState.hosts), + postDownloadState.downloadDirectory, + postDownloadState.postId, + postDownloadState.status.name, + postDownloadState.threadId, + postDownloadState.postTitle, + postDownloadState.threadTitle, + postDownloadState.forum, + postDownloadState.total, + postDownloadState.url, + postDownloadState.token, + Timestamp.valueOf(postDownloadState.addedOn), + postDownloadState.rank + ) + eventBus.publishEvent(Event(Event.Kind.POST_UPDATE, id)) + return postDownloadState.copy(id = id) + } + + override fun findByPostId(postId: String): Optional { + val post = jdbcTemplate.query( + "SELECT metadata.*,post.* FROM METADATA metadata FULL JOIN POST post ON metadata.POST_ID_REF = post.ID WHERE post.POST_ID = ?", + PostRowMapper(), + postId + ) + return if (post.isEmpty()) { + Optional.empty() + } else { + Optional.of(post[0]) + } + } + + override fun findCompleted(): List { + return jdbcTemplate.query( + "SELECT POST_ID FROM POST AS post WHERE status = ? AND done >= total", + { rs: ResultSet, _: Int -> rs.getString("POST_ID") }, + Status.FINISHED.name + ) + } + + override fun findById(id: Long): Optional { + val post = jdbcTemplate.query( + "SELECT metadata.*,post.* FROM METADATA metadata FULL JOIN POST post ON metadata.POST_ID_REF = post.ID WHERE post.ID = ?", + PostRowMapper(), + id + ) + return if (post.isEmpty()) { + Optional.empty() + } else { + Optional.of(post[0]) + } + } + + override fun findAll(): List { + return jdbcTemplate.query( + "SELECT metadata.*,post.* FROM METADATA metadata FULL JOIN POST post ON metadata.POST_ID_REF = post.ID", + PostRowMapper() + ) + } + + override fun existByPostId(postId: String): Boolean { + val count = jdbcTemplate.queryForObject( + "SELECT COUNT(*) FROM POST AS post WHERE post.POST_ID = ?", Int::class.java, postId + ) + return count > 0 + } + + override fun setDownloadingToStopped(): Int { + return jdbcTemplate.update( + "UPDATE POST AS post SET post.STATUS = ? WHERE post.STATUS = ? OR post.STATUS = ?", + Status.STOPPED.name, + Status.DOWNLOADING.name, + Status.PENDING.name + ) + } + + override fun deleteByPostId(postId: String): Int { + val mutationCount = jdbcTemplate.update("DELETE FROM POST AS post WHERE post.POST_ID = ?", postId) + eventBus.publishEvent(Event(Event.Kind.POST_REMOVE, postId)) + return mutationCount + } + + override fun update(postDownloadState: PostDownloadState) { + jdbcTemplate.update( + "UPDATE POST AS post SET post.STATUS = ?, post.DONE = ?, post.RANK = ? WHERE post.ID = ?", + postDownloadState.status.name, + postDownloadState.done, + postDownloadState.rank, + postDownloadState.id + ) + eventBus.publishEvent(Event(Event.Kind.POST_UPDATE, postDownloadState.id)) + } +} + +internal class PostRowMapper : RowMapper { + @Throws(SQLException::class) + override fun mapRow(rs: ResultSet, rowNum: Int): PostDownloadState { + val id = rs.getLong("post.ID") + val status = Status.valueOf(rs.getString("post.STATUS")) + val postId = rs.getString("post.POST_ID") + val threadId = rs.getString("post.THREAD_ID") + val postTitle = rs.getString("post.POST_TITLE") + val threadTitle = rs.getString("post.THREAD_TITLE") + val forum = rs.getString("post.FORUM") + val url = rs.getString("post.URL") + val token = rs.getString("post.TOKEN") + val done = rs.getInt("post.DONE") + val total = rs.getInt("post.TOTAL") + val hosts = rs.getString("post.HOSTS").split(DELIMITER).dropLastWhile { it.isEmpty() }.toSet() + val downloadDirectory = rs.getString("post.POST_FOLDER_NAME") + val addedOn = rs.getTimestamp("post.ADDED_ON").toLocalDateTime() + val rank = rs.getInt("post.RANK") +// val metadataId = rs.getLong("metadata.POST_ID_REF") +// if (!rs.wasNull()) { +// val metadata = Metadata() +// metadata.postIdRef = metadataId +// metadata.postId = rs.getString("metadata.POST_ID") +// val resolvedNames = rs.getString("metadata.RESOLVED_NAMES") +// if (resolvedNames != null && resolvedNames.isNotBlank()) { +// metadata.resolvedNames = +// resolvedNames.split("%sep%").dropLastWhile { it.isEmpty() } +// } +// metadata.postedBy = rs.getString("metadata.POSTED_BY") +// postDownloadState.metadata = metadata +// } + return PostDownloadState( + id, postTitle, threadTitle, forum, url, token, postId, threadId, total, hosts, downloadDirectory, addedOn, status, done, rank + ) + } + + companion object { + private const val DELIMITER = ";" + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/repositories/impl/ThreadRepositoryImpl.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/repositories/impl/ThreadRepositoryImpl.kt new file mode 100644 index 00000000..80160e01 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/repositories/impl/ThreadRepositoryImpl.kt @@ -0,0 +1,87 @@ +package me.mnlr.vripper.repositories.impl + +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.jdbc.core.JdbcTemplate +import org.springframework.jdbc.core.RowMapper +import org.springframework.stereotype.Service +import me.mnlr.vripper.entities.Thread +import me.mnlr.vripper.event.Event +import me.mnlr.vripper.event.EventBus +import me.mnlr.vripper.repositories.ThreadRepository +import java.sql.ResultSet +import java.sql.SQLException +import java.util.* + +@Service +class ThreadRepositoryImpl @Autowired constructor( + private val jdbcTemplate: JdbcTemplate, + private val eventBus: EventBus +) : ThreadRepository { + @Synchronized + private fun nextId(): Long { + return jdbcTemplate.queryForObject("CALL NEXT VALUE FOR SEQ_THREAD", Long::class.java)!! + } + + override fun save(thread: Thread): Thread { + val id = nextId() + jdbcTemplate.update( + "INSERT INTO THREAD (ID, TOTAL, LINK, THREAD_ID) values (?,?,?,?)", + id, + thread.total, + thread.link, + thread.threadId + ) + eventBus.publishEvent(Event(Event.Kind.THREAD_UPDATE, id)) + return thread.copy(id = id) + } + + override fun findByThreadId(threadId: String): Optional { + val threadList = jdbcTemplate.query( + "SELECT * FROM THREAD AS thread WHERE thread.THREAD_ID = ?", + ThreadRowMapper(), + threadId + ) + return if (threadList.isEmpty()) { + Optional.empty() + } else { + Optional.of(threadList[0]) + } + } + + override fun findAll(): List { + return jdbcTemplate.query("SELECT * FROM THREAD", ThreadRowMapper()) + } + + override fun findById(id: Long): Optional { + val threadList = jdbcTemplate.query( + "SELECT * FROM THREAD AS thread WHERE thread.ID = ?", ThreadRowMapper(), id + ) + return if (threadList.isEmpty()) { + Optional.empty() + } else { + Optional.of(threadList[0]) + } + } + + override fun deleteByThreadId(threadId: String): Int { + val mutationCount = + jdbcTemplate.update("DELETE FROM THREAD AS thread WHERE THREAD_ID = ?", threadId) + eventBus.publishEvent(Event(Event.Kind.THREAD_REMOVE, threadId)) + return mutationCount + } + + override fun deleteAll() { + jdbcTemplate.update("DELETE FROM THREAD") + } +} + +internal class ThreadRowMapper : RowMapper { + @Throws(SQLException::class) + override fun mapRow(rs: ResultSet, rowNum: Int): Thread { + val id = rs.getLong("ID") + val link = rs.getString("LINK") + val threadId = rs.getString("THREAD_ID") + val total = rs.getInt("TOTAL") + return Thread(id, link, threadId, total) + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/services/DataTransaction.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/services/DataTransaction.kt new file mode 100644 index 00000000..bbe908b3 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/services/DataTransaction.kt @@ -0,0 +1,149 @@ +package me.mnlr.vripper.services + +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import me.mnlr.vripper.entities.ImageDownloadState +import me.mnlr.vripper.entities.Metadata +import me.mnlr.vripper.entities.PostDownloadState +import me.mnlr.vripper.entities.Thread +import me.mnlr.vripper.entities.domain.Status +import me.mnlr.vripper.model.PostItem +import me.mnlr.vripper.repositories.ImageRepository +import me.mnlr.vripper.repositories.MetadataRepository +import me.mnlr.vripper.repositories.PostDownloadStateRepository +import me.mnlr.vripper.repositories.ThreadRepository +import kotlin.io.path.pathString + +@Service +@Transactional +class DataTransaction( + private val postDownloadStateRepository: PostDownloadStateRepository, + private val imageRepository: ImageRepository, + private val threadRepository: ThreadRepository, + private val metadataRepository: MetadataRepository, + private val settingsService: SettingsService, + private val pathService: PathService +) { + private fun save(postDownloadState: PostDownloadState): PostDownloadState { + return postDownloadStateRepository.save(postDownloadState) + } + + fun update(postDownloadState: PostDownloadState) { + postDownloadStateRepository.update(postDownloadState) + } + + fun save(thread: Thread): Thread { + return threadRepository.save(thread) + } + + private fun save(imageDownloadState: ImageDownloadState): ImageDownloadState { + return imageRepository.save(imageDownloadState) + } + + fun update(imageDownloadState: ImageDownloadState) { + imageRepository.update(imageDownloadState) + } + + fun exists(postId: String): Boolean { + return postDownloadStateRepository.existByPostId(postId) + } + + fun newPost(postItem: PostItem): PostDownloadState { + val postDownloadState = save(PostDownloadState( + postTitle = postItem.title, + url = postItem.url, + token = postItem.securityToken, + postId = postItem.postId, + threadId = postItem.threadId, + total = postItem.imageCount, + hosts = postItem.hosts.keys.map { it.host }.toSet(), + threadTitle = postItem.threadTitle, + forum = postItem.forum, + downloadDirectory = pathService.calculateDownloadPath(postItem.forum, postItem.threadTitle, postItem.title, postItem.postId, settingsService.settings).pathString + )) + val images: MutableList = mutableListOf() + postItem.imageItemList.forEachIndexed { index, imageItem -> + val imageDownloadState = ImageDownloadState( + postId = postItem.postId, + url = imageItem.mainLink, + host = imageItem.host, + index = index, + postIdRef = postDownloadState.id!! + ) + images.add(save(imageDownloadState)) + } + sortPostsByRank() + return postDownloadState + } + + fun finishPost(postDownloadState: PostDownloadState) { + if (imageRepository.findByPostIdAndIsError(postDownloadState.postId).isNotEmpty()) { + postDownloadState.status = Status.ERROR + update(postDownloadState) + } else { + if (postDownloadState.done < postDownloadState.total) { + postDownloadState.status = Status.STOPPED + update(postDownloadState) + } else { + postDownloadState.status = Status.FINISHED + update(postDownloadState) + if (settingsService.settings.downloadSettings.clearCompleted) { + remove(listOf(postDownloadState.postId)) + } + } + } + } + + private fun remove(postIds: List) { + for (postId in postIds) { + imageRepository.deleteAllByPostId(postId) + metadataRepository.deleteByPostId(postId) + postDownloadStateRepository.deleteByPostId(postId) + } + sortPostsByRank() + } + + fun removeThread(threadId: String) { + threadRepository.deleteByThreadId(threadId) + } + + fun clearCompleted(): List { + val completed = postDownloadStateRepository.findCompleted() + remove(completed) + return completed + } + + fun removeAll(postIds: List?) { + if (postIds != null) { + remove(postIds) + } else { + remove(postDownloadStateRepository.findAll().map(PostDownloadState::postId)) + } + } + + fun stopImagesByPostIdAndIsNotCompleted(postId: String) { + imageRepository.stopByPostIdAndIsNotCompleted(postId) + } + + @Synchronized + fun setMetadata(postDownloadState: PostDownloadState, metadata: Metadata) { + if (metadataRepository.findByPostId(postDownloadState.postId).isEmpty) { + metadata.postIdRef = postDownloadState.id + metadataRepository.save(metadata) + } + } + + fun clearQueueLinks() { + threadRepository.deleteAll() + } + + @Synchronized + fun sortPostsByRank() { + val postDownloadState = + postDownloadStateRepository.findAll().sortedWith(Comparator.comparing(PostDownloadState::addedOn)) + for (i in postDownloadState.indices) { + postDownloadState[i].rank = i + update(postDownloadState[i]) + } + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/services/DownloadSpeedService.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/services/DownloadSpeedService.kt new file mode 100644 index 00000000..f1fe9edb --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/services/DownloadSpeedService.kt @@ -0,0 +1,30 @@ +package me.mnlr.vripper.services + +import org.springframework.scheduling.annotation.Scheduled +import org.springframework.stereotype.Service +import me.mnlr.vripper.event.Event +import me.mnlr.vripper.event.EventBus +import java.util.concurrent.atomic.AtomicLong + +@Service +class DownloadSpeedService(private val eventBus: EventBus) { + private val read = AtomicLong(0) + var currentValue = 0L + private var allowWrite = false + fun increase(read: Long) { + if (allowWrite) { + this.read.addAndGet(read) + } + } + + @Scheduled(fixedDelay = 1000) + private fun calc() { + allowWrite = false + val newValue = read.getAndSet(0) + if (newValue != currentValue) { + currentValue = newValue + eventBus.publishEvent(Event(Event.Kind.BYTES_PER_SECOND, currentValue)) + } + allowWrite = true + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/services/GlobalStateService.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/services/GlobalStateService.kt new file mode 100644 index 00000000..8419508b --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/services/GlobalStateService.kt @@ -0,0 +1,44 @@ +package me.mnlr.vripper.services + +import org.springframework.scheduling.annotation.Scheduled +import org.springframework.stereotype.Service +import me.mnlr.vripper.download.DownloadService +import me.mnlr.vripper.event.Event +import me.mnlr.vripper.event.EventBus +import me.mnlr.vripper.formatSI +import me.mnlr.vripper.model.GlobalState +import me.mnlr.vripper.repositories.ImageRepository + +@Service +class GlobalStateService( + private val downloadService: DownloadService, + private val imageRepository: ImageRepository, + private val eventBus: EventBus, + private val vgAuthService: VGAuthService, + private val downloadSpeedService: DownloadSpeedService +) { + private var currentState: GlobalState = newValue() + + @Scheduled(fixedDelay = 200) + private fun interval() { + val newGlobalState = newValue() + if (newGlobalState != currentState) { + currentState = newGlobalState + eventBus.publishEvent(Event(Event.Kind.DOWNLOAD_STATUS, currentState)) + } + } + + private fun newValue(): GlobalState { + return GlobalState( + downloadService.runningCount(), + downloadService.pendingCount(), + imageRepository.countError(), + vgAuthService.loggedUser, + downloadSpeedService.currentValue.formatSI() + ) + } + + fun get(): GlobalState { + return currentState.copy() + } +} diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/services/HTTPService.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/services/HTTPService.kt new file mode 100644 index 00000000..19ec75ab --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/services/HTTPService.kt @@ -0,0 +1,131 @@ +package me.mnlr.vripper.services + +import jakarta.annotation.PreDestroy +import org.apache.http.client.config.CookieSpecs +import org.apache.http.client.config.RequestConfig +import org.apache.http.client.methods.AbstractExecutionAwareRequest +import org.apache.http.client.methods.HttpGet +import org.apache.http.client.methods.HttpHead +import org.apache.http.client.methods.HttpPost +import org.apache.http.client.protocol.HttpClientContext +import org.apache.http.impl.client.HttpClientBuilder +import org.apache.http.impl.client.HttpClients +import org.apache.http.impl.client.LaxRedirectStrategy +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager +import org.springframework.scheduling.annotation.Scheduled +import org.springframework.stereotype.Service +import reactor.core.Disposable +import me.mnlr.vripper.event.Event +import me.mnlr.vripper.event.EventBus +import me.mnlr.vripper.model.Settings +import java.net.URI +import java.util.concurrent.TimeUnit + +@Service +class HTTPService(eventBus: EventBus, settingsService: SettingsService) { + private val disposable: Disposable + private lateinit var pcm: PoolingHttpClientConnectionManager + private lateinit var rc: RequestConfig + private var connectionTimeout: Int = settingsService.settings.connectionSettings.timeout + + init { + disposable = eventBus + .flux() + .filter { it.kind == Event.Kind.SETTINGS_UPDATE } + .map { it.data as Settings } + .subscribe { + if (connectionTimeout != it.connectionSettings.timeout) { + connectionTimeout = it.connectionSettings.timeout + buildRequestConfig() + } + } + buildRequestConfig() + buildConnectionPool() + } + + @PreDestroy + private fun destroy() { + disposable.dispose() + } + + private fun buildConnectionPool() { + pcm = PoolingHttpClientConnectionManager() + pcm.maxTotal = Int.MAX_VALUE + pcm.defaultMaxPerRoute = Int.MAX_VALUE + } + + private fun buildRequestConfig() { + rc = RequestConfig.custom() + .setConnectionRequestTimeout(connectionTimeout * 1000) + .setConnectTimeout(connectionTimeout * 1000) + .setSocketTimeout(connectionTimeout * 1000) + .setCookieSpec(CookieSpecs.STANDARD) + .build() + } + + @Scheduled(fixedDelay = 15000) + private fun idleConnectionMonitoring() { + pcm.closeIdleConnections(60, TimeUnit.SECONDS) + } + + val client: HttpClientBuilder + get() = HttpClients.custom() + .setConnectionManager(pcm) + .setRedirectStrategy(LaxRedirectStrategy()) + .disableAutomaticRetries() + .setDefaultRequestConfig(rc) + + fun buildHttpGet(url: String, context: HttpClientContext): HttpGet { + val httpGet = HttpGet(url.replace(" ", "+")) + httpGet.addHeader("User-Agent", USER_AGENT) + addToContext(context, httpGet) + return httpGet + } + + fun buildHttpHead(url: String, context: HttpClientContext): HttpHead { + val httpHead = HttpHead(url.replace(" ", "+")) + httpHead.addHeader("User-Agent", USER_AGENT) + addToContext(context, httpHead) + return httpHead + } + + fun buildHttpPost(url: String, context: HttpClientContext): HttpPost { + val httpPost = HttpPost(url.replace(" ", "+")) + httpPost.addHeader("User-Agent", USER_AGENT) + addToContext(context, httpPost) + return httpPost + } + + fun buildHttpGet(uri: URI, context: HttpClientContext): HttpGet { + val httpGet = HttpGet(uri) + httpGet.addHeader("User-Agent", USER_AGENT) + addToContext(context, httpGet) + return httpGet + } + + fun addToContext(context: HttpClientContext, request: AbstractExecutionAwareRequest) { + val contextAttributes = + context.getAttribute( + ContextAttributes.CONTEXT_ATTRIBUTES, + ContextAttributes::class.java + ) + if (contextAttributes != null) { + synchronized(contextAttributes.requests) { + contextAttributes.requests.add(request) + } + } + } + + class ContextAttributes { + val requests: MutableList = mutableListOf() + + companion object { + const val CONTEXT_ATTRIBUTES = "CONTEXT_ATTRIBUTES" + } + } + + companion object { + const val USER_AGENT = + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36" + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/services/HtmlProcessorService.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/services/HtmlProcessorService.kt new file mode 100644 index 00000000..72e066b4 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/services/HtmlProcessorService.kt @@ -0,0 +1,24 @@ +package me.mnlr.vripper.services + +import org.htmlcleaner.CleanerProperties +import org.htmlcleaner.DomSerializer +import org.htmlcleaner.HtmlCleaner +import org.springframework.stereotype.Service +import org.w3c.dom.Document +import me.mnlr.vripper.exception.HtmlProcessorException +import java.io.InputStream + +@Service +class HtmlProcessorService { + @Throws(HtmlProcessorException::class) + fun clean(htmlContent: InputStream): Document { + return try { + htmlContent.use { + val clean = HtmlCleaner().clean(htmlContent) + DomSerializer(CleanerProperties()).createDOM(clean) + } + } catch (e: Exception) { + throw HtmlProcessorException(e) + } + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/services/MetadataService.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/services/MetadataService.kt new file mode 100644 index 00000000..ac7252a4 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/services/MetadataService.kt @@ -0,0 +1,38 @@ +package me.mnlr.vripper.services + +import org.springframework.stereotype.Service +import me.mnlr.vripper.entities.PostDownloadState +import me.mnlr.vripper.tasks.MetadataRunnable +import java.util.function.Consumer + +@Service +class MetadataService(private val threadPoolService: ThreadPoolService) { + private val fetchingMetadata: MutableMap = mutableMapOf() + + @Synchronized + fun startFetchingMetadata(postDownloadState: PostDownloadState) { + val runnable = MetadataRunnable(postDownloadState) + threadPoolService.generalExecutor.submit(runnable) + fetchingMetadata[postDownloadState.postId] = runnable + } + + @Synchronized + fun stopFetchingMetadata(postIds: List) { + val stopping: MutableList = ArrayList() + for ((key, value) in fetchingMetadata) { + if (postIds.contains(key)) { + stopping.add(value) + value.stop() + } + } + while (stopping.isNotEmpty()) { + stopping.removeIf { it.finished } + try { + Thread.sleep(100) + } catch (e: InterruptedException) { + Thread.currentThread().interrupt() + } + } + postIds.forEach(Consumer { key: String -> fetchingMetadata.remove(key) }) + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/services/PathService.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/services/PathService.kt new file mode 100644 index 00000000..345e7bc8 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/services/PathService.kt @@ -0,0 +1,49 @@ +package me.mnlr.vripper.services + +import org.springframework.stereotype.Service +import me.mnlr.vripper.delegate.LoggerDelegate +import me.mnlr.vripper.model.Settings +import java.io.File +import java.nio.file.Path + +@Service +class PathService { + private val log by LoggerDelegate() + + fun calculateDownloadPath(forum: String, threadTitle: String, postTitle: String, postId: String, settings: Settings): Path { + var downloadDirectory = + if (settings.downloadSettings.forumSubfolder) Path.of(settings.downloadSettings.downloadPath, sanitize(forum)) else Path.of( + settings.downloadSettings.downloadPath + ) + downloadDirectory = if (settings.downloadSettings.threadSubLocation) downloadDirectory.resolve(threadTitle) else downloadDirectory + downloadDirectory = downloadDirectory.resolve(if (settings.downloadSettings.appendPostId) "${sanitize(postTitle)}_${postId}" else sanitize( + postTitle + )) + return downloadDirectory + } + + private fun makeDir(sourceFolder: File): File { + var counter = 1 + var folder = sourceFolder + while (folder.exists()) { + folder = File(sourceFolder.toString() + '.' + counter++) + } + if (!folder.mkdirs()) { + throw RuntimeException(String.format("Failed to create the folder %s", sourceFolder)) + } + return folder + } + + /** + * Will sanitize the image name and remove extension + * + * @param path + * @return Sanitized local path string + */ + fun sanitize(path: String): String { + val sanitizedPath = + path.replace("\\.|\\\\|/|\\||:|\\?|\\*|\"|<|>|\\p{Cntrl}".toRegex(), "_") + log.debug(String.format("%s sanitized to %s", path, sanitizedPath)) + return sanitizedPath + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/services/RetryPolicyService.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/services/RetryPolicyService.kt new file mode 100644 index 00000000..70489f17 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/services/RetryPolicyService.kt @@ -0,0 +1,54 @@ +package me.mnlr.vripper.services + +import jakarta.annotation.PreDestroy +import net.jodah.failsafe.RetryPolicy +import net.jodah.failsafe.event.ExecutionAttemptedEvent +import org.springframework.stereotype.Service +import reactor.core.Disposable +import me.mnlr.vripper.delegate.LoggerDelegate +import me.mnlr.vripper.event.Event +import me.mnlr.vripper.event.EventBus +import me.mnlr.vripper.model.Settings +import java.time.temporal.ChronoUnit + +@Service +class RetryPolicyService(eventBus: EventBus, settingsService: SettingsService) { + private val log by LoggerDelegate() + private val disposable: Disposable + private var maxAttempts: Int = settingsService.settings.connectionSettings.maxAttempts + + init { + disposable = eventBus + .flux() + .filter { it.kind == Event.Kind.SETTINGS_UPDATE } + .map { it.data as Settings } + .subscribe { + if (maxAttempts != it.connectionSettings.maxAttempts) { + maxAttempts = it.connectionSettings.maxAttempts + } + } + } + + fun buildRetryPolicyForDownload(): RetryPolicy { + return RetryPolicy() + .withDelay(2, 5, ChronoUnit.SECONDS) + .withMaxAttempts(maxAttempts) + .onFailedAttempt { + log.warn("#${it.attemptCount} tries failed", it.lastFailure) + } + } + + fun buildGenericRetryPolicy(): RetryPolicy { + return RetryPolicy() + .withDelay(2, 5, ChronoUnit.SECONDS) + .withMaxAttempts(maxAttempts) + .onFailedAttempt { e: ExecutionAttemptedEvent -> + log.warn("#${e.attemptCount} tries failed", e.lastFailure) + } + } + + @PreDestroy + private fun destroy() { + disposable.dispose() + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/services/SettingsService.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/services/SettingsService.kt new file mode 100644 index 00000000..3eda73de --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/services/SettingsService.kt @@ -0,0 +1,211 @@ +package me.mnlr.vripper.services + +import com.fasterxml.jackson.databind.DeserializationFeature +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory +import com.fasterxml.jackson.module.kotlin.readValue +import com.fasterxml.jackson.module.kotlin.registerKotlinModule +import jakarta.annotation.PostConstruct +import jakarta.annotation.PreDestroy +import org.apache.commons.codec.digest.DigestUtils +import org.springframework.beans.factory.annotation.Value +import org.springframework.core.io.Resource +import org.springframework.stereotype.Service +import me.mnlr.vripper.SpringContext +import me.mnlr.vripper.delegate.LoggerDelegate +import me.mnlr.vripper.event.Event +import me.mnlr.vripper.event.EventBus +import me.mnlr.vripper.exception.ValidationException +import me.mnlr.vripper.model.Settings +import java.io.FileWriter +import java.io.IOException +import java.nio.file.* + +@Service +class SettingsService( + @Value("\${base.dir}") baseDir: String, + @Value("\${base.dir.name}") baseDirName: String, + private val eventBus: EventBus, + @Value("classpath:proxies.json") private val defaultProxies: Resource +) { + private val log by LoggerDelegate() + private val configPath: Path + private val customProxiesPath: Path + private val om = ObjectMapper(YAMLFactory()) + private val proxies: MutableSet = HashSet() + + + var settings = Settings() + + init { + configPath = Paths.get(baseDir, baseDirName, "config.yml") + customProxiesPath = Paths.get(baseDir, baseDirName, "proxies.json") + om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + .findAndRegisterModules().registerKotlinModule() + } + + @PostConstruct + private fun init() { + loadViperProxies() + restore() + eventBus.publishEvent(Event(Event.Kind.SETTINGS_UPDATE, settings)) + } + + private fun loadViperProxies() { + try { + defaultProxies.inputStream.use { defaultProxiesIs -> + val defaultProxies: List = om.readValue(defaultProxiesIs) + val customProxies: List = if (customProxiesPath.toFile() + .exists() && Files.isRegularFile(customProxiesPath) + ) { + om.readValue( + customProxiesPath.toFile() + ) + } else { + emptyList() + } + + try { + FileWriter(customProxiesPath.toFile()).use { fw -> + fw.append("[]").append(System.lineSeparator()) + fw.flush() + } + } catch (e: IOException) { + log.warn( + "Unable to create " + customProxiesPath.toFile().absolutePath, e + ) + } + + proxies.addAll(customProxies) + proxies.addAll(defaultProxies) + } + } catch (e: IOException) { + log.error("Failed to load vipergirls proxies list", e) + } + proxies.add("https://vipergirls.to") + } + + fun getProxies(): List { + return ArrayList(proxies) + } + + fun newSettings(settings: Settings) { + if (settings.viperSettings.login) { + if (this.settings.viperSettings.password != settings.viperSettings.password) { + settings.viperSettings.password = DigestUtils.md5Hex(settings.viperSettings.password) + } + } else { + settings.viperSettings.username = "" + settings.viperSettings.password = "" + settings.viperSettings.thanks = false + settings.viperSettings.login = false + } + check(settings) + this.settings = settings + save() + eventBus.publishEvent(Event(Event.Kind.SETTINGS_UPDATE, settings)) + } + + fun restore() { + try { + if (configPath.toFile().exists()) { + settings = om.readValue(configPath.toFile()) + } + } catch (e: IOException) { + log.error("Failed restore user settings", e) + settings = Settings() + } + if (!proxies.contains(settings.viperSettings.host)) { + settings.viperSettings.host = "https://vipergirls.to" + } + try { + check(settings) + } catch (e: ValidationException) { + log.error( + String.format( + "Your settings are invalid, either remove %s, or fix it", configPath + ), e + ) + SpringContext.close() + } + save() + } + + fun save() { + try { + Files.write( + configPath, + om.writeValueAsBytes(settings), + StandardOpenOption.CREATE, + StandardOpenOption.WRITE, + StandardOpenOption.TRUNCATE_EXISTING, + StandardOpenOption.SYNC + ) + } catch (e: IOException) { + log.error("Failed to store user settings", e) + } + } + + @PreDestroy + private fun destroy() { + save() + } + + @Throws(ValidationException::class) + fun check(settings: Settings) { + val path: Path = try { + Paths.get(settings.downloadSettings.downloadPath) + } catch (e: InvalidPathException) { + throw ValidationException(String.format("%s is invalid", settings.downloadSettings.downloadPath)) + } + if (!Files.exists(path)) { + throw ValidationException(String.format("%s does not exist", settings.downloadSettings.downloadPath)) + } else if (!Files.isDirectory(path)) { + throw ValidationException(String.format("%s is not a directory", settings.downloadSettings.downloadPath)) + } + + if(settings.downloadSettings.autoQueueThreshold < 0) { + throw ValidationException("Invalid auto queue settings, value must be a positive integer") + } + + if (settings.connectionSettings.maxTotalThreads < 0 || settings.connectionSettings.maxTotalThreads > 12) { + throw ValidationException( + String.format( + "Invalid max global concurrent download settings, values must be in [%d,%d]", + 0, + 12 + ) + ) + } + if (settings.connectionSettings.maxThreads < 1 || settings.connectionSettings.maxThreads > 4) { + throw ValidationException( + String.format( + "Invalid max concurrent download settings, values must be in [%d,%d]", 1, 4 + ) + ) + } + if (settings.connectionSettings.timeout < 1 || settings.connectionSettings.timeout > 300) { + throw ValidationException( + String.format( + "Invalid connection timeout settings, values must be in [%d,%d]", 1, 300 + ) + ) + } + if (settings.connectionSettings.maxAttempts < 1 || settings.connectionSettings.maxAttempts > 10) { + throw ValidationException( + String.format( + "Invalid maximum attempts settings, values must be in [%d,%d]", 1, 10 + ) + ) + } + if (settings.maxEventLog < 100 || settings.maxEventLog > 10000) { + throw ValidationException( + String.format( + "Invalid maximum event log record settings, values must be in [%d,%d]", + 100, + 10000 + ) + ) + } + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/services/ThreadCacheService.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/services/ThreadCacheService.kt new file mode 100644 index 00000000..c44e17a5 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/services/ThreadCacheService.kt @@ -0,0 +1,39 @@ +package me.mnlr.vripper.services + +import com.github.benmanes.caffeine.cache.Caffeine +import com.github.benmanes.caffeine.cache.LoadingCache +import jakarta.annotation.PreDestroy +import org.springframework.stereotype.Service +import reactor.core.Disposable +import me.mnlr.vripper.event.Event +import me.mnlr.vripper.event.EventBus +import me.mnlr.vripper.model.ThreadItem +import me.mnlr.vripper.parser.ThreadLookupAPIParser +import java.util.* +import java.util.concurrent.ExecutionException +import java.util.concurrent.TimeUnit + +@Service +class ThreadCacheService(eventBus: EventBus) { + private val cache: LoadingCache = + Caffeine.newBuilder().expireAfterWrite(5, TimeUnit.MINUTES) + .build { threadId: String -> + ThreadLookupAPIParser(threadId).parse() + } + private val disposable: Disposable + + init { + disposable = eventBus.flux().filter { e: Event<*> -> e.kind == Event.Kind.SETTINGS_UPDATE } + .subscribe { cache.invalidateAll() } + } + + @PreDestroy + private fun destroy() { + disposable.dispose() + } + + @Throws(ExecutionException::class) + operator fun get(threadId: String): ThreadItem { + return cache[threadId] ?: throw NoSuchElementException("$threadId does not exist") + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/services/ThreadPoolService.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/services/ThreadPoolService.kt new file mode 100644 index 00000000..8be9b8e1 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/services/ThreadPoolService.kt @@ -0,0 +1,17 @@ +package me.mnlr.vripper.services + +import org.springframework.stereotype.Service +import java.util.concurrent.ExecutorService +import java.util.concurrent.Executors +import java.util.concurrent.TimeUnit + +@Service +class ThreadPoolService { + val generalExecutor: ExecutorService = Executors.newFixedThreadPool(4) + fun destroy() { + generalExecutor.shutdown() + if (!generalExecutor.awaitTermination(5, TimeUnit.SECONDS)) { + generalExecutor.shutdownNow() + } + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/services/VGAuthService.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/services/VGAuthService.kt new file mode 100644 index 00000000..3e5c2cc3 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/services/VGAuthService.kt @@ -0,0 +1,142 @@ +package me.mnlr.vripper.services + +import jakarta.annotation.PostConstruct +import jakarta.annotation.PreDestroy +import org.apache.http.NameValuePair +import org.apache.http.client.entity.UrlEncodedFormEntity +import org.apache.http.client.protocol.HttpClientContext +import org.apache.http.cookie.Cookie +import org.apache.http.impl.client.BasicCookieStore +import org.apache.http.message.BasicNameValuePair +import org.apache.http.util.EntityUtils +import org.springframework.stereotype.Service +import reactor.core.Disposable +import me.mnlr.vripper.delegate.LoggerDelegate +import me.mnlr.vripper.entities.PostDownloadState +import me.mnlr.vripper.event.Event +import me.mnlr.vripper.event.EventBus +import me.mnlr.vripper.exception.VripperException +import me.mnlr.vripper.model.PostItem +import me.mnlr.vripper.tasks.LeaveThanksRunnable + +@Service +class VGAuthService( + private val cm: HTTPService, + private val settingsService: SettingsService, + private val threadPoolService: ThreadPoolService, + private val eventBus: EventBus +) { + private val log by LoggerDelegate() + private val disposable: Disposable + + final val context: HttpClientContext = HttpClientContext.create() + var loggedUser = "" + + var authenticated = false + + init { + context.cookieStore = BasicCookieStore() + disposable = eventBus + .flux() + .filter { it.kind == Event.Kind.SETTINGS_UPDATE } + .subscribe { authenticate() } + } + + @PostConstruct + private fun init() { + authenticate() + } + + @PreDestroy + private fun destroy() { + disposable.dispose() + } + + fun authenticate() { + authenticated = false + if (!settingsService.settings.viperSettings.login) { + log.debug("Authentication option is disabled") + context.cookieStore.clear() + loggedUser = "" + eventBus.publishEvent(Event(Event.Kind.VG_USER, loggedUser)) + return + } + val username = settingsService.settings.viperSettings.username + val password = settingsService.settings.viperSettings.password + if (username.isEmpty() || password.isEmpty()) { + log.error("Cannot authenticate with ViperGirls credentials, username or password is empty") + context.cookieStore.clear() + loggedUser = "" + eventBus.publishEvent(Event(Event.Kind.VG_USER, loggedUser)) + return + } + val postAuth = + cm.buildHttpPost(settingsService.settings.viperSettings.host + "/login.php?do=login", context) + val params: MutableList = ArrayList() + params.add(BasicNameValuePair("vb_login_username", username)) + params.add(BasicNameValuePair("cookieuser", "1")) + params.add(BasicNameValuePair("do", "login")) + params.add(BasicNameValuePair("vb_login_md5password", password)) + try { + postAuth.entity = UrlEncodedFormEntity(params) + } catch (e: Exception) { + context.cookieStore.clear() + loggedUser = "" + eventBus.publishEvent(Event(Event.Kind.VG_USER, loggedUser)) + log.error("Failed to authenticate user with " + settingsService.settings.viperSettings.host, e) + return + } + postAuth.addHeader("Referer", settingsService.settings.viperSettings.host) + postAuth.addHeader( + "Host", + settingsService.settings.viperSettings.host.replace("https://", "").replace("http://", "") + ) + val client = cm.client.build() + try { + client.execute(postAuth, context).use { response -> + if (response.statusLine.statusCode / 100 != 2) { + throw VripperException( + String.format( + "Unexpected response code returned %s", response.statusLine.statusCode + ) + ) + } + val responseBody = EntityUtils.toString(response.entity) + log.debug( + String.format( + "Authentication with ViperGirls response body:%n%s", + responseBody + ) + ) + EntityUtils.consumeQuietly(response.entity) + if (context.cookieStore.cookies.stream() + .map { obj: Cookie -> obj.name } + .noneMatch { e: String -> e == "vg_userid" } + ) { + log.error( + String.format( + "Failed to authenticate user with %s, missing vg_userid cookie", + settingsService.settings.viperSettings.host + ) + ) + return + } + } + } catch (e: Exception) { + context.cookieStore.clear() + loggedUser = "" + eventBus.publishEvent(Event(Event.Kind.VG_USER, loggedUser)) + log.error("Failed to authenticate user with " + settingsService.settings.viperSettings.host, e) + return + } + authenticated = true + loggedUser = username + log.info(String.format("Authenticated: %s", username)) + eventBus.publishEvent(Event(Event.Kind.VG_USER, loggedUser)) + } + + fun leaveThanks(post: PostDownloadState) { + threadPoolService.generalExecutor + .submit(LeaveThanksRunnable(post, authenticated, context)) + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/services/XpathService.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/services/XpathService.kt new file mode 100644 index 00000000..d2f52a57 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/services/XpathService.kt @@ -0,0 +1,31 @@ +package me.mnlr.vripper.services + +import org.springframework.stereotype.Service +import org.w3c.dom.Node +import org.w3c.dom.NodeList +import me.mnlr.vripper.exception.XpathException +import javax.xml.xpath.XPathConstants +import javax.xml.xpath.XPathFactory + +@Service +class XpathService { + private val xpath = XPathFactory.newInstance().newXPath() + + @Throws(XpathException::class) + fun getAsNode(source: Node?, xpathExpression: String?): Node? { + return try { + xpath.compile(xpathExpression).evaluate(source, XPathConstants.NODE) as Node + } catch (e: Exception) { + throw XpathException(e) + } + } + + @Throws(XpathException::class) + fun getAsNodeList(source: Node?, xpathExpression: String?): NodeList { + return try { + xpath.compile(xpathExpression).evaluate(source, XPathConstants.NODESET) as NodeList + } catch (e: Exception) { + throw XpathException(e) + } + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/tasks/LeaveThanksRunnable.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/tasks/LeaveThanksRunnable.kt new file mode 100644 index 00000000..75824bb3 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/tasks/LeaveThanksRunnable.kt @@ -0,0 +1,118 @@ +package me.mnlr.vripper.tasks + +import org.apache.http.NameValuePair +import org.apache.http.client.entity.UrlEncodedFormEntity +import org.apache.http.client.methods.HttpPost +import org.apache.http.client.protocol.HttpClientContext +import org.apache.http.impl.client.CloseableHttpClient +import org.apache.http.message.BasicNameValuePair +import org.apache.http.util.EntityUtils +import me.mnlr.vripper.SpringContext +import me.mnlr.vripper.delegate.LoggerDelegate +import me.mnlr.vripper.entities.LogEvent +import me.mnlr.vripper.entities.LogEvent.Status.* +import me.mnlr.vripper.entities.PostDownloadState +import me.mnlr.vripper.formatToString +import me.mnlr.vripper.repositories.LogEventRepository +import me.mnlr.vripper.services.HTTPService +import me.mnlr.vripper.services.SettingsService +import java.io.UnsupportedEncodingException + +class LeaveThanksRunnable( + private val postDownloadState: PostDownloadState, + private val authenticated: Boolean, + private val context: HttpClientContext +) : Runnable { + private val log by LoggerDelegate() + private val cm: HTTPService = SpringContext.getBean(HTTPService::class.java) + private val settingsService: SettingsService = SpringContext.getBean(SettingsService::class.java) + private val eventRepository: LogEventRepository = SpringContext.getBean(LogEventRepository::class.java) + private val logEvent: LogEvent + + init { + logEvent = eventRepository.save( + LogEvent( + type = LogEvent.Type.THANKS, status = PENDING, message = "Leaving thanks for $postDownloadState.url" + ) + ) + } + + override fun run() { + try { + eventRepository.update(logEvent.copy(status = PROCESSING)) + if (!settingsService.settings.viperSettings.login) { + eventRepository.update( + logEvent.copy( + status = DONE, + message = "Will not send a like for ${postDownloadState.url}\nAuthentication with ViperGirls option is disabled" + ) + ) + return + } + if (!settingsService.settings.viperSettings.thanks) { + eventRepository.update( + logEvent.copy( + status = DONE, + message = "Will not send a like for ${postDownloadState.url}\nLeave thanks option is disabled" + ) + ) + return + } + if (!authenticated) { + eventRepository.update( + logEvent.copy( + status = ERROR, + message = "Will not send a like for ${postDownloadState.url}\nYou are not authenticated" + ) + ) + return + } + val postThanks: HttpPost = cm.buildHttpPost( + "${settingsService.settings.viperSettings.host}/post_thanks.php", HttpClientContext.create() + ) + val params: MutableList = ArrayList() + params.add(BasicNameValuePair("do", "post_thanks_add")) + params.add(BasicNameValuePair("using_ajax", "1")) + params.add(BasicNameValuePair("p", postDownloadState.postId)) + params.add(BasicNameValuePair("securitytoken", postDownloadState.token)) + try { + postThanks.entity = UrlEncodedFormEntity(params) + } catch (e: UnsupportedEncodingException) { + val error = "Request error for ${postDownloadState.url}" + log.error(error, e) + eventRepository.update( + logEvent.copy( + status = ERROR, message = """ + $error + ${e.formatToString()} + """.trimIndent() + ) + ) + return + } + postThanks.addHeader("Referer", settingsService.settings.viperSettings.host) + postThanks.addHeader( + "Host", settingsService.settings.viperSettings.host.replace("https://", "").replace("http://", "") + ) + val client: CloseableHttpClient = cm.client.build() + client.execute(postThanks, context).use { response -> + try { + } finally { + EntityUtils.consumeQuietly(response.entity) + } + } + eventRepository.update(logEvent.copy(status = DONE)) + } catch (e: Exception) { + val error = "Failed to leave a thanks for $postDownloadState" + log.error(error, e) + eventRepository.update( + logEvent.copy( + status = ERROR, message = """ + $error + ${e.formatToString()} + """.trimIndent() + ) + ) + } + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/tasks/LinkScanRunnable.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/tasks/LinkScanRunnable.kt new file mode 100644 index 00000000..33517fff --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/tasks/LinkScanRunnable.kt @@ -0,0 +1,110 @@ +package me.mnlr.vripper.tasks + +import me.mnlr.vripper.SpringContext +import me.mnlr.vripper.delegate.LoggerDelegate +import me.mnlr.vripper.entities.LogEvent +import me.mnlr.vripper.entities.LogEvent.Status.* +import me.mnlr.vripper.formatToString +import me.mnlr.vripper.model.ThreadInput +import me.mnlr.vripper.repositories.impl.LogEventRepositoryImpl +import me.mnlr.vripper.services.SettingsService +import me.mnlr.vripper.services.ThreadPoolService +import java.time.LocalDateTime +import java.util.regex.Pattern + +class LinkScanRunnable(private val urlList: List) : Runnable { + private val log by LoggerDelegate() + private val settingsService: SettingsService = + SpringContext.getBean(SettingsService::class.java) + private val threadPoolService: ThreadPoolService = + SpringContext.getBean(ThreadPoolService::class.java) + private val eventRepository: LogEventRepositoryImpl = + SpringContext.getBean(LogEventRepositoryImpl::class.java) + private val logEvent: LogEvent + + init { + logEvent = eventRepository.save( + LogEvent( + type = LogEvent.Type.SCAN, + status = PENDING, + time = LocalDateTime.now(), + message = "Links to scan:\n\t${urlList.joinToString("\n\t")}" + ) + ) + } + + override fun run() { + try { + eventRepository.update(logEvent.copy(status = PROCESSING)) + val threadInputList = mutableListOf() + val unsupported = mutableListOf() + val unrecognized = mutableListOf() + for (link in urlList) { + log.debug("Starting to process thread: $link") + if (!link.startsWith(settingsService.settings.viperSettings.host)) { + log.error("Unsupported link $link") + unsupported.add(link) + continue + } + var threadId: String + val m = Pattern.compile( + Pattern.quote(settingsService.settings.viperSettings.host) + "/threads/(\\d+).*" + ).matcher(link) + if (m.find()) { + threadId = m.group(1) + } else { + log.error("Cannot retrieve thread id from URL $link") + unrecognized.add(link) + continue + } + threadInputList.add(ThreadInput(link = link, threadId = threadId)) + } + val errorMessage: String = if (unsupported.isNotEmpty()) { + """Unsupported links: + ${unsupported.joinToString("\n\t")} + """ + } else if (unrecognized.isNotEmpty()) { + """Unrecognized links: + ${unrecognized.joinToString("\n\t")} + """ + } else { + "" + } + if (errorMessage.isNotBlank()) { + eventRepository.update( + logEvent.copy( + status = ERROR, message = "Some links failed to be scanned: \n$errorMessage" + ) + ) + } + for (threadInput in threadInputList) { +// if (thread.postId.isNotBlank()) { +// threadPoolService.generalExecutor.submit( +// SinglePostRunnable( +// thread.threadId, thread.postId +// ) +// ) +// } else { + threadPoolService.generalExecutor.submit( + ThreadLookupRunnable( + threadInput, + settingsService.settings + ) + ) +// } + } + eventRepository.update(logEvent.copy(status = DONE)) + } catch (e: Exception) { + val error = "Error when scanning links" + log.error(error, e) + eventRepository.update( + logEvent.copy( + status = ERROR, message = """ + $error + ${e.formatToString()} + """.trimIndent() + ) + ) + } + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/tasks/MetadataRunnable.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/tasks/MetadataRunnable.kt new file mode 100644 index 00000000..d32a7d94 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/tasks/MetadataRunnable.kt @@ -0,0 +1,177 @@ +package me.mnlr.vripper.tasks + +import org.apache.http.client.HttpClient +import org.apache.http.client.methods.CloseableHttpResponse +import org.apache.http.client.methods.HttpGet +import org.apache.http.client.protocol.HttpClientContext +import org.apache.http.util.EntityUtils +import org.w3c.dom.Document +import org.w3c.dom.Node +import me.mnlr.vripper.SpringContext +import me.mnlr.vripper.delegate.LoggerDelegate +import me.mnlr.vripper.entities.LogEvent +import me.mnlr.vripper.entities.LogEvent.Status.* +import me.mnlr.vripper.entities.Metadata +import me.mnlr.vripper.entities.PostDownloadState +import me.mnlr.vripper.exception.DownloadException +import me.mnlr.vripper.formatToString +import me.mnlr.vripper.repositories.LogEventRepository +import me.mnlr.vripper.services.* +import java.util.concurrent.atomic.AtomicBoolean +import java.util.stream.Collectors + +class MetadataRunnable(private val postDownloadState: PostDownloadState) : Runnable { + + private val log by LoggerDelegate() + private val dataTransaction: DataTransaction = + SpringContext.getBean(DataTransaction::class.java) + private val eventRepository: LogEventRepository = + SpringContext.getBean(LogEventRepository::class.java) + private val cm: HTTPService = SpringContext.getBean(HTTPService::class.java) + private val htmlProcessorService: HtmlProcessorService = + SpringContext.getBean(HtmlProcessorService::class.java) + private val xpathService: XpathService = SpringContext.getBean(XpathService::class.java) + private val metadataService: MetadataService = + SpringContext.getBean(MetadataService::class.java) + private val context: HttpClientContext = HttpClientContext.create() + private val logEvent: LogEvent + + @Volatile + private var stopped = false + + @Volatile + var finished = false + + init { + val vgAuthService: VGAuthService = SpringContext.getBean(VGAuthService::class.java) + context.cookieStore = vgAuthService.context.cookieStore + context.setAttribute( + HTTPService.ContextAttributes.CONTEXT_ATTRIBUTES, HTTPService.ContextAttributes() + ) + logEvent = eventRepository.save( + LogEvent( + type = LogEvent.Type.METADATA, + status = PENDING, + message = "Fetching metadata for " + postDownloadState.url + ) + ) + } + + override fun run() { + try { + eventRepository.update(logEvent.copy(status = PROCESSING)) + val metadata = fetchMetadata(postDownloadState.postId, postDownloadState.url) + dataTransaction.setMetadata(postDownloadState, metadata) + eventRepository.update(logEvent.copy(status = DONE)) + } catch (e: Exception) { + val message = "Failed to fetch metadata for ${postDownloadState.url}" + log.error(message, e) + eventRepository.update( + logEvent.copy( + status = ERROR, message = """ + $message + ${e.formatToString()} + """.trimIndent() + ) + ) + } finally { + if (stopped) { + val message = "Fetching metadata for ${postDownloadState.url} interrupted" + eventRepository.update(logEvent.copy(status = DONE, message = message)) + } + finished = true + metadataService.stopFetchingMetadata(listOf(postDownloadState.postId)) + } + } + + private fun fetchMetadata(postId: String, url: String): Metadata { + val httpGet: HttpGet = cm.buildHttpGet(url, context) + val connection: HttpClient = cm.client.build() + (connection.execute(httpGet, context) as CloseableHttpResponse).use { response -> + try { + if (response.statusLine.statusCode / 100 != 2) { + throw DownloadException("Unexpected response code '${response.statusLine.statusCode}' for $httpGet") + } + val document: Document = + htmlProcessorService.clean(response.entity.content) + val postNode: Node? = + xpathService.getAsNode( + document, + "//li[@id='post_$postId']/div[contains(@class, 'postdetails')]" + ) + val postedBy: String = xpathService.getAsNode( + postNode, + "./div[contains(@class, 'userinfo')]//a[contains(@class, 'username')]//font" + )?.textContent?.trim() ?: "" + + val metadata = Metadata() + metadata.postedBy = postedBy + + val node: Node? = xpathService.getAsNode( + document, "//div[@id='post_message_$postId']" + ) + if (node != null) { + metadata.resolvedNames = findTitleInContent(node) + } else { + metadata.resolvedNames = emptyList() + } + metadata.postId = postId + return metadata + } finally { + EntityUtils.consumeQuietly(response.entity) + } + } + } + + private fun findTitleInContent(node: Node): List { + val altTitle: MutableList = ArrayList() + findTitle(node, altTitle, AtomicBoolean(true)) + return altTitle.stream().distinct().collect(Collectors.toList()) + } + + private fun findTitle(node: Node, altTitle: MutableList, keepGoing: AtomicBoolean) { + if (!keepGoing.get()) { + return + } + if (node.nodeName == "a" || node.nodeName == "img") { + keepGoing.set(false) + return + } + if (node.nodeType == Node.ELEMENT_NODE) { + for (i in 0 until node.childNodes.length) { + val item = node.childNodes.item(i) + findTitle(item, altTitle, keepGoing) + if (!keepGoing.get()) { + return + } + } + } else if (node.nodeType == Node.TEXT_NODE) { + val text = node.textContent.trim() + if (text.isNotBlank() && dictionary.stream().noneMatch { e: String -> + text.lowercase().contains(e.lowercase()) + }) { + altTitle.add(text) + } + } + } + + fun stop() { + stopped = true + val contextAttributes: HTTPService.ContextAttributes? = context.getAttribute( + HTTPService.ContextAttributes.CONTEXT_ATTRIBUTES, + HTTPService.ContextAttributes::class.java + ) + if (contextAttributes != null) { + synchronized(contextAttributes.requests) { + for (request in contextAttributes.requests) { + request.abort() + } + } + } + } + + companion object { + private val dictionary: List = + mutableListOf("download", "link", "rapidgator", "filefactory", "filefox") + } +} \ No newline at end of file diff --git a/vripper-core/src/main/kotlin/me/mnlr/vripper/tasks/ThreadLookupRunnable.kt b/vripper-core/src/main/kotlin/me/mnlr/vripper/tasks/ThreadLookupRunnable.kt new file mode 100644 index 00000000..880d63c5 --- /dev/null +++ b/vripper-core/src/main/kotlin/me/mnlr/vripper/tasks/ThreadLookupRunnable.kt @@ -0,0 +1,106 @@ +package me.mnlr.vripper.tasks + +import me.mnlr.vripper.SpringContext +import me.mnlr.vripper.delegate.LoggerDelegate +import me.mnlr.vripper.download.DownloadService +import me.mnlr.vripper.entities.LogEvent +import me.mnlr.vripper.entities.LogEvent.Status.* +import me.mnlr.vripper.entities.Thread +import me.mnlr.vripper.formatToString +import me.mnlr.vripper.model.Settings +import me.mnlr.vripper.model.ThreadInput +import me.mnlr.vripper.model.ThreadItem +import me.mnlr.vripper.repositories.LogEventRepository +import me.mnlr.vripper.repositories.ThreadRepository +import me.mnlr.vripper.services.DataTransaction +import me.mnlr.vripper.services.SettingsService +import me.mnlr.vripper.services.ThreadCacheService + +class ThreadLookupRunnable(private val threadInput: ThreadInput, private val settings: Settings) : Runnable { + private val log by LoggerDelegate() + private val dataTransaction = SpringContext.getBean(DataTransaction::class.java) + private val eventRepository = SpringContext.getBean(LogEventRepository::class.java) + private val threadCacheService = SpringContext.getBean( + ThreadCacheService::class.java + ) + private val threadRepository = SpringContext.getBean( + ThreadRepository::class.java + ) + private val downloadService = SpringContext.getBean(DownloadService::class.java) + private val logEvent: LogEvent + + init { + logEvent = eventRepository.save( + LogEvent( + type = LogEvent.Type.THREAD, + status = LogEvent.Status.PENDING, + message = "Processing multi-post link ${threadInput.link}" + ) + ) + } + + override fun run() { + try { + eventRepository.update(logEvent.copy(status = PROCESSING)) + val threadLookupResult = threadCacheService[threadInput.threadId] + if (threadLookupResult.postItemList.isEmpty()) { + val message = "Nothing found for " + threadInput.link + eventRepository.update(logEvent.copy(status = ERROR, message = message)) + return + } +// if (threadItemList.size == 1) { +// threadPoolService.generalExecutor.submit( +// SinglePostRunnable( +// threadItemList[0].postId, threadItemList[0].threadId +// ) +// ) +// eventRepository.update( +// logEvent.copy( +// status = DONE, +// message = "Thread ${thread.link} is added to the queue" +// ) +// ) +// } else { + if (threadRepository.findByThreadId(threadInput.threadId).isEmpty) { + dataTransaction.save( + Thread( + link = threadInput.link, + threadId = threadInput.threadId, + total = threadLookupResult.postItemList.size + ) + ) + eventRepository.update( + logEvent.copy( + status = DONE, message = "New thread ${threadInput.link} is added" + ) + ) + autostart(threadLookupResult) + } else { + log.info("Link ${threadInput.link} is already loaded") + eventRepository.update( + logEvent.copy( + status = ERROR, message = "${threadInput.link} has already been added to the queue" + ) + ) + } +// } + } catch (e: Exception) { + val error = "Error when adding multi-post link ${threadInput.link}" + log.error(error, e) + eventRepository.update( + logEvent.copy( + status = ERROR, message = """ + $error + ${e.formatToString()} + """.trimIndent() + ) + ) + } + } + + private fun autostart(lookupResult: ThreadItem) { + if(lookupResult.postItemList.size <= settings.downloadSettings.autoQueueThreshold) { + downloadService.download(lookupResult.postItemList.map { Pair(it.threadId, it.postId) }) + } + } +} \ No newline at end of file diff --git a/vripper-server/src/main/resources/application.properties b/vripper-core/src/main/resources/application.properties similarity index 95% rename from vripper-server/src/main/resources/application.properties rename to vripper-core/src/main/resources/application.properties index 45ed67d2..e5304200 100644 --- a/vripper-server/src/main/resources/application.properties +++ b/vripper-core/src/main/resources/application.properties @@ -1,6 +1,5 @@ logging.level.org.springframework.web=INFO logging.level.root=INFO -logging.level.org.apache.http=INFO logging.level.org.springframework.web.socket.config.WebSocketMessageBrokerStats=ERROR logging.file.name=${base.dir}/${base.dir.name}/vripper.log server.port=${vripper.server.port:8080} @@ -14,3 +13,4 @@ spring.datasource.password=lEtmEIn spring.datasource.driver-class-name=org.hsqldb.jdbc.JDBCDriver base.dir=${user.dir} base.dir.name=${vripper.base.dir.name:vripper} +download.pool-size=12 \ No newline at end of file diff --git a/vripper-core/src/main/resources/db/changelog/db.changelog-master.xml b/vripper-core/src/main/resources/db/changelog/db.changelog-master.xml new file mode 100644 index 00000000..9de31ef7 --- /dev/null +++ b/vripper-core/src/main/resources/db/changelog/db.changelog-master.xml @@ -0,0 +1,145 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vripper-core/src/main/resources/logback-spring.xml b/vripper-core/src/main/resources/logback-spring.xml new file mode 100644 index 00000000..8ae0c47c --- /dev/null +++ b/vripper-core/src/main/resources/logback-spring.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/vripper-server/src/main/resources/proxies.json b/vripper-core/src/main/resources/proxies.json similarity index 100% rename from vripper-server/src/main/resources/proxies.json rename to vripper-core/src/main/resources/proxies.json diff --git a/vripper-electron/.gitignore b/vripper-electron/.gitignore deleted file mode 100644 index c55b0c95..00000000 --- a/vripper-electron/.gitignore +++ /dev/null @@ -1,47 +0,0 @@ -# See http://help.github.com/ignore-files/ for more about ignoring files. - -# compiled output -/dist -/tmp -/out-tsc - -# dependencies -/node_modules - -# profiling files -chrome-profiler-events.json -speed-measure-plugin.json - -# IDEs and editors -/.idea -.project -.classpath -.c9/ -*.launch -.settings/ -*.sublime-workspace - -# IDE - VSCode -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json -.history/* - -# misc -/.sass-cache -/connect.lock -/coverage -/libpeerconnection.log -npm-debug.log -yarn-error.log -testem.log -/typings - -# System Files -.DS_Store -Thumbs.db - -dist -build/vripper-ui \ No newline at end of file diff --git a/vripper-electron/.jshintrc b/vripper-electron/.jshintrc deleted file mode 100644 index 1814afde..00000000 --- a/vripper-electron/.jshintrc +++ /dev/null @@ -1,7 +0,0 @@ -{ - "esversion": 6, - "node": true, - "globals": { - "BigInt": false - } -} diff --git a/vripper-electron/build/icon.icns b/vripper-electron/build/icon.icns deleted file mode 100644 index 52f6591031d606e0b62ec014318dbc8c5cf13069..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 43326 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 E0pw-Hd;kCd diff --git a/vripper-electron/build/icon.png b/vripper-electron/build/icon.png deleted file mode 100644 index 8c1e926d560dfc30641b9d1dc5c007b57ff226b7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2103 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 diff --git a/vripper-electron/build/icons/32x32.png b/vripper-electron/build/icons/32x32.png deleted file mode 100644 index 1edd5535dd52cc1b06dd700ea6c63a47d57dead9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1092 zcmV-K1iSl*P)98GO&h zym_5>=goUJ2EQxL;oS56&b{Z{d!B|OEx0r=)PWPJr(gS`f1fKNAxyo?zEeJ9{aO##*~ANvU8wg}*>BDj<^cgbKwH*XU^F6Mn2DtSPWx* zT@GCV+LNFQfB>A8$pMT)f%(x9V_dgs{Jwx36!_gOMqN69>@vcg-T>Lod(mi?dC*+Kx3(U z9R+Bb%w;mF0-g(NjPk|XnHh3VpW3zv25C+tXlQ96)Y@wM|9$j`x3jZK>?S-Gz^HGe z1-Z52-QC0w9rBFvf`C7S2{(bf$^y4KknaS=_5;Y9kQT|(%QUF6;?39?#ib>ufP)8Z zyRf*(;`q1{y9U1}VMPE47@#yjdcxe;VD8Qx+ujn7)0{}yww}pgzI&&_Ul8!C#SPZz zd|qW`)dQpbKh^2#=l|Prcy0>-$d}&dhkh%wsg$D~%PzU_@dV8wQQEZ`}^4vk30Rk&ieekANefs?NP9&6t}T_mjT0+2FT6CG|-oX$x3I{ zTVT!Yoxt5qB!2+xlgV8L03_jc77`_Af!l~ z2QCL-pj_>>;47d)NEqld;jjjKC=D`|pXV<$m@wc^4N}$x=Zp87h$@Xe`MQPx0000< KMNUMnLSTYp=>GNq diff --git a/vripper-electron/build/icons/48x48.png b/vripper-electron/build/icons/48x48.png deleted file mode 100644 index d2476789b44efa3c802bc2b6af47eea3bbe49413..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1578 zcmV+_2G#kAP)KD z4@Lopn;3im)Wm3v`~gH0A0R&Xq7@SpF~ts-pedJ7(x#yV2#AJ23I$4lkY0hdbAh%q zb9^|{GMBT@ZCc`&%*$SD?ftFUYwgS0M=%Bm!8G7)0V`E_6PN{ffzp&p1b7Uz3%Hqt zivpU0(3$Vp!l~gg@D9T^0o#G)4!gN5;Ghd?*J}>ihHVFq!fOfm8rTU;ax$n6qADB^ zus;Zo>=;^hpau#(@EPzeFG6Fi5mRB`WcY3!3|cX;HleAwB*ze(7%8QRPg z6*Gl8*Z^GN<&g(itiY8Jyl65X3mGF!1`gN3Cq_zA2M)t-;8&nvjEUKS zOTdqZ;d7aSZVuGJ2BF2gYuu>WLqdg(o8Wl1Ajg3a%mMxe+%JQf4MY@Ju@P>jH)e!T z2#SCs{Qt-UlqR9J1`5*~v*Y_K3|5;-GiNe$%NAYPP+uSIH8qx)OkcN-DGL|s${rj# zMC`!>ndy=;_yX8J(l8oAA$S$I1(e9F3*Bzs?d?S=EX1vZAl;3Pc4?r(DkXJ? zwS{;%Oz-*gx-z3AmCa61b2ITs#4Zg4%u~?LKDHs7^3vI}DV{b>S0;17#`2ksxe8F` z#L{p%)d{2GiV8|*&C=Dkv3zEuOu^x0W#GmQV)yUs%JgF0S6!{E8@+draGUAPQHIH; zS4tyvo<6PHt`{n@+~}yU&xZmETG7c7WU=d6x)fJ&aYoxX#6Lh-0iHUw?ml~#~ zLzyrE&$1VLqifv%Lu&$)BDvh+MC_xn+bigI+i z8yiW)Vorw#xUGn+E1F%%meX_xWZ~mr_sxI`x0EDY%%>9>314+J2 zz~a#;y)7+HhXaVZw8ER_zRjBaO$b-n} zQ9@|77RR%omj^;om^fobu5O;YuSly4jXDs7M=E^dv{ySxna}bmU~l@{TE=0n555Qf zanf&{1E{(9@@eKL^zZQe3tLwbEw>q}7NJ>;kM+YsVL=fL$NJFM2}hg(e6;10MtN z@nU8H2?3wV$b*G?P#wG{;5W24tCxuZV0#e$GE%Zs&^iKhliJR=WGt9jz*S&VDjyAD z=}x>6Zchb2@FigW0N@-k74`yER>*_1PYtFgVUMaMx#4?)4MfovjD2=L0LklPk2q`r zK17=xyM3v^B^7=ba3}~*obYh$Kst`WD{)w(!YZMaDqjbtlKHt!4{%R~n*uHtK+}5Y c&Uftp0QAAZ^2-&;`2YX_07*qoM6N<$f)vQm-2eap diff --git a/vripper-electron/build/icons/512x512.png b/vripper-electron/build/icons/512x512.png deleted file mode 100644 index 5823446a13be6162112817a61d3b20d5ba30ce08..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17957 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 Y@_PA;7=*D>IWQ80a`AF*ydL`C|6tO3XaE2J literal 0 HcmV?d00001 diff --git a/vripper-gui/src/main/resources/icons/128x128.png b/vripper-gui/src/main/resources/icons/128x128.png new file mode 100644 index 0000000000000000000000000000000000000000..e6db8fa95ccd3cd14787046d0c4f594b2be080e1 GIT binary patch literal 3802 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 Qod5s;07*qoM6N<$g3Z)8`~Uy| literal 0 HcmV?d00001 diff --git a/vripper-gui/src/main/resources/icons/16x16.png b/vripper-gui/src/main/resources/icons/16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..878807a16b6c541f2e0720d44566462439be2c4d GIT binary patch literal 478 zcmV<40U`d0P)Jz>CWNIP!9mVopcf7Y;?!*BhP=tddC)9)3C*l=$&1&{>GgEn z?^CF|V2}sY0s-WH2LseBcqLEBF5C`Z7#DXk85F-{vz&X9B;?xZv}P3M-G?`LP?O8s zzB?VxeKZ=#)%<=<07DZ3KGkHwYNhk(6b?{`v)>9*Br&4$`)N?W^n&Br*@J(cdk>OG; zC~j}lTb~b6bUMX=@-ZP>JU|s9$>KO#z@I6xVpbQdCu|rKH|75w0YD#>mouyC53S6! UzO7)QtN;K207*qoM6N<$ff);=Wn%4=1IfzMYhEK9JTcc z2-+CRj>%^}tiR2;v#5s%k}=PPhN_^9)Dt;iPZRdslUvd&KMbKBhWb&BKVREY%qRT4 zVSflNxS1IPdHV9Cm|ES1gU0QA4XQocAv4@NYr0ooltNqLE{D7QG1sGH5}#SgtkQck z2Rh6)GXSxdKpN$D*P4Os>P0*U{o7+9oRfFag#&Tv`5|BO%L^Hwl3LS#2Z?qHBz)i`%y4BpVT`Da$AsG!KWkbC z_70fG##4yxkpfv{(2;;3yHsp#o--uOIRFdp&i9hqJ_>R2Xh8 zk|2OhdMTN4NEuOWm<5T|-R&p_nOWoU0R^(ibbxIK0WpP6B zT2mkI>_f(gTiSm1=R-gVklCve!a=Poq!Mj4xa?bT;TxiG%wpo3Id_gdj7i`hgKC=~ zc@jUqv_TAEEKgZv)FTuB9#(wi?kpl@@#U5^9$qT6_Hdc$1*7Wg9~yZIoPi%$003)M zvcvJtaK-vt8l)2=(Fl%ci?pnyrVOo`F5ZCw$#;boCiSc;pE3WmL{2WYNyss<;>uVq z5&kROx=}DhJWN~JhAgj9KX39m;wlO=mB0?3DP=KAOCdkfb8&_Id&P#i9pm96ei4=^ z&ILNIHHf?wvIy7(ToB!bBf3){u~WB^eDT`T(BW4B%>Z?Bk5)fZUeTeKHFL40|D-H33iZC?u;h~yi3DX zul3aOu>tPFdDLNPmjnTo;Ia@!$}iKp2mp4tA$sM4dsJ8Q*R{Sfh8IRkWmrJAc5;3P zq@sW_<=W`riwzL?%?*56IJt;|g1IL}`~l>iL6$Pr>qBDj} zw>Qbkhzn2@DS+uUej8K$lpVF+Ooo9c=`|@4OH>?xmr!&Cz?1ReyogWqI|At8$UNgE z4&zdI{`fgDP<!Tew21;+>3gN3u99W%gCZ!~>l zvNta3Dnn{&kUkXPYWr@#oFPSLS8__%1Q7NuQJr5lf-(@GPBIB?uxE;f^ycUAF#rl3 z-)43j!}Ke^D^=+;)UIL4SG!LbS65Bf=yKIGf9W6BCbGFsAqjeq{vH@A*#H0& z@aE|T`2-@X-_~?qfL>d#d;5(9i@zD`$s?yx6 z?h|4`Gu&O@UiE?*5=wt%c{VW&Rk@;)c(`UZ%fj(6jyP)-b_p zZoE~lC0f9;9Sn^%Lhln@+Uo1bj+s1OyTR_so6{ODiCw%IHvoC4%Lyue;b2tr| z#%)>8wAz-6>K*-bbOa7_2-hA9kS^r3EoX%*HE^7TGO)ulbEK^rao*p6AbJ`t%p6QP z>*gs$Nf%=k`Xw<|-g(*Tu{a~OQWNeKiU)3CiwZ(Nkc~)e1bsj3X#|osItA)ZufB$~nZ9uhH%^`DF z342~T{q5?VhPTvqX>P)_3Ll3BwA)Mwd)rI z$B{eW*SW4z8ap#R8<=iw$D70O6k&LAy}wjP-$}w#JMXtq6hQn8@#n-iBQs-s*HnK3 zHbdFvd+aP*0{B_9#Rgv%na7Bkz*3DiL?DElfKwkSssQMS1nX1K4hB{*p1a=e<8QBC=XS(cm~@VOcNw~xF@9e;lQ)6b#T zWaauWe=#T~E_^0;iq&-Kb1mPDo${q4=Dh#n7V8iOtJ=Jw@q~YqK!_xFa7?^+kr!*4nLTuO4A7WT57Wte4j^U3iC z$tBaJTMyc!NxoYC7YZ{@G}|4g3JbUQXNzFf&oVzVq*P(PUyB)$gN=*oi#^_Z1ERPg zH|L>lvO@!(dd@zoG?ia1EW}$T(YY{*gmKzia`N3>;SG^IBTbAV-Ua%Mr~tr+r=0=>HMPu%-Fe=?@e=~S#3p7~{P?=RO| z=rwznB(l^HnB!)F{#32D{O3`tR($iUT*ezRrFXmnuI1MJS&!<&zE#R8(^Mi5#T$bYMN@je<7^?C&<;YmTNH@}Dnj z!D*BuUUW?+Qd@mTI`CXOhbB$?X|?Wb9Z-v87WLmoCvw*8MvIn?fxhbJ;HrQbOz)-) zj>aBjpyxTC@xI;V@+i2I5DnQpG~$?7|c|{7L#eBdgBkx*@a< zG3A4nASKvC$b2mGgrL0?GJ&cTHc&Y0B94kAEp_1C^&H1hW2!=69S1{i?p2tzg@Rc2 zc1j83C;y(+jmsh5%-HF!O8i&b>aKF~gm|GhoW{<6b4;1}JEsPb)wq8rb4cp&!|$>& z(189YxB*OI?ph9I3dgmj@Pix+K~YF#Xr_&w{DtyI1+tr#Y^F&0R8_r@z;BW{a=Q2j zcY+R&aGdRcE|$tD&X0VS{g$JjMC*Pns=;ZnI~TQpt?wqtX6Pk8{1rwaaIC3^VnE-T z1uX18o08vX;q6{5FGWW$R8#WbUF*-#-!f;MSWO`h{Qx5SG25YuUW=-uP{ZKBv#uPq znmL@R+xzn}End2SDgJJ`Hz0iyjmKn-CEp9oMzi-1M`}%R?2)YbUrgS&;>*3mRVeH6 z!|l#w15r?O8#?{tj7w(C z_8y5?D5Cr$w?{NXKUefQG!TU|FwuC>4=FdQiZR=ZJQxo35%U-R13PX2{wzoh7tOgNkic1NcU$Z(Azcg-sr?m&IROicxdKIdE z+o1|GCuREKMRO3Ko_Vj)ZiNM=>zrMT-6%n-pu~=?PhmjI7 zhtc6_%!(WJGowu4ht$0nm{^`mt^`LIAEYPKU(t{-wp&hqw>3QkrH> z#lSRQ6P3Yw-`S-l70a&JxcaEwc-Y+=^H8LE)t)$>H(>_+Q@%xt)e#=pg$a;|?Pn@chU4 zKS)}}3gGSfku6wBZ98@>SoG}g1RGA2YxP-Puucl}0W#=h0WTXvR)uH_j9!#kNY9u{ z(Okg`rj+glEZZ&aFkkA&6NceV`|seOf!5l0PH5oMX5SZZ>-nz3dYNGAizY$<6UJ-d z#(;PKh43K#=sbrW4V0al40poi4U10Dt@=jGiq1bZ=LcFS#$~djpbA*-xt|8&e{hs1H9;Z{g|+KGn6sCP3+}jdc)0yNX+SSXV>kz6{^>ZD>XvtGqCT(L z#ZGLFxFB?{y$)P@PWSd#S&m>TSyQt75y5W^9AY|y3`*)LlRp-{LC|X$d;7VMKL*6I z9=#aurzZr7gYryGA>%;WhQ5S*w*8fch5&ecU5n#{(jAXww!(W%ZgP5}7i;KO6#i>g zhI4NzKE}d38a6|SmJ;V+0I*>S6dayGJ4hX78AB>hKqPSvrfo2Ku{T03>SZZ(m1VwP z6huiH2y<9NKOA8Mm~%TD&Bebl@AarVThfFd;JicK83%A#wb|^e&ycG#PCf|wYY*J) z95w^#1g^@u&cm;r+X( z^QYyVM)c*)mZ0BhjX#d2T^W_n{{PILH$>bFd-wI+LLNUW~|j`4t>{Bdx?> z<$*fvb=f0_IN)p>L$aOo+eXK`3eFRlN8WR#vo-NkvnEA2)`pTKY8kzoTM(b6(*tg5 zPq(Z_Th8f%CfIWt?6=v+I!XKZFMq~fV{a%?hZ)@q0)A+SW~0GZ!7y6d**>>5W>h6Y zBPLCJi_h;@O{Y^CnoLIzJZMryW2q>VFN+?-?^^o7=O3){UptJK7S{VGYop9-7vG2= zSwa#0NLpemzo0M&ibFp+t^zyl$G%1j*9R}18P>eenHbP|)4qT6)`@m_Kk{QiX32N) z7~*hEs>eNMG-JG_ud@`yj@i&__YN@E>Qt}<#<c3pKz%-TkH`uj0KL0j zL_>y%zF_VzT8G*a?ZYYB3#1oOdcx;7ylPdX#AxW3-UkozD?U!gRTlm?t zDAg9gq11t5wBJ0Z_PC%&;A|7GAI_wEpVc^ixvPd%NsVs#6>LO%nJreNz;OMs9>%u> z;35448B&%Z82&>MDY75YY^QniwE<&-?ER~=ui{z($J{fd7ufkXdlJwa+&HmM9=EJ- zCO*)6WuVc&og8vViq)yjQ&)xKP>t*!%uZtvD`;`{rx7 zLxdI0Re}=z8hUp}-+Z)3cuRC1XZ!sSbvSOV0AI%CSq+EWWfW{8YNjU>!5mbGz{vM* z`_~id$=LSnObxK>ueGJ4DKZO_cYo=<#vAU+?ag`Rzq+4P8dyNjRp&>eV;BM1*D?nb zwC8>CxEwEaIF^a&*!qtXZlA^lYHP?1kY73E8*{JSz8oS6!@Urx*ZSkRVFLF7fdS6v z%XL?7ea?mo;ppO&k|8Nm;0DNbbovM3F7j=rkkPiP=YE@20aB?7)UQGPySe`xL2>;R zX&;c{X#tjXN17!>(wG7}POo!wP>=UH6nymO zQ02DSTszM!hVz|oT7-;Hdq8C^;F!JPo~ZSI==I2`a@7Xyn^|;Y;DEg6$amyLH4eSd zk!y5AyhTu2y*SVFdm%VLuF)0SFnu1SY$YK48`v4OzUSfkPpR@h$#UkgiT}|Ws_%us zvA{N|(RNES!IFmbo>@m$?j36g+m-DDruDuabOk@blaWp`Q{=;B z9%i9~ro58H(drSH%545XE~b-Hx|i43LMuTIg#-zgL12W*5{^`ApCG?o(ToHJn5GMW4 zN|?so8Wv zaDE)+ne^HyxnHE!Qg7#I3x`_p>Qqr_AAGjGvL3(@tJtLb(d;liG(g3_uh!>mnk`@! z47jo9_3fLs(ovRX<|BQwF=%1!@hkT5oLfhhTkc9_Z@qQ?7^@Rz4(-T~`)_mMjr=y| z8K#@&Ni0!ZfC<_oL2{k)2=dn*Emfu&F%K|;%L>p2fC?^DfRq3dTM(`(=J64+lkoR+ zgVrs1qsu-H&99+H4I}CSQ4z4)U5$pMhK#+0(3`>d&kRuJK9_y|j`7edm5twACm5aO zNz=x&D!5fz)8Y&arQ`n^HpEVDAG7T1$8H%+&mVou4)N3JpzW1 za>HNL6-RD*2w_6((LL4_IIecI-L))}xs>GLZi@vjavD!cmcua<_I=y*y|4m!x6x@R z#tfk_`AYjf7gZhQFx>lWnW59WWIB8N*ZtFHt<6}*8(i=OiJsaoM_83k;?CPWhQm$aCf~1BRhPq`bc2= zX{25%8(2}Wy4uA;F;$3k64_Gk-vWvu?1^$3k1L+k(BL6R219rS)pKl?a~@pDpwdaJ zE<(g)QBH?DEYobIpV`16z2~CF0le_21LBw-Nh=W4{mW z`*5_;y2sLzUKWrlv3K5Y1=BhDbJ}(2dVu}%dD1&gm^nzLgPU6NlZu1`L5hr+n4Px4 zFnRL0a6~EB%XM(*FESYdWI+)Kq#%||&k5&hHM{=dWQLI6ovTA72=`+))K0Pt4cXhs z+fIi(_(Ii+mMYgh7bkJa9HaJDl$kC6B_tC_4y7V_Vxq2V2pTZ>l`lhyh-I$NBxM_> z+BJY07k!Kjz=A~NV%^l5bHcfO%+~w+jl@!g>KtUFA=gCkwxTzMPqKpHNz)Z`F2^FX z&Ig#HN|NyC>5{qXbV#gTQTe!H#4#;8eG)EEtaxlDBRj7KK_rpavl$C{NHA%UuXs(bzESdHhoacqv?d6zRfP7oV zQ+|g{WsPfhcudiDA;CY@o35F29@Y|fIlwSf>bi_ae@)yq&7tywrvBEB7b?^fy#&$z zKbnw3B#V<+)og-|CCFYX%68f3-*oZiA^$%}h2U7$5EA}r4y*q3-L&e7unsFf&$aik zU(6&RqAX8p0_{VWx4KniV$ql`{LkC5k7~}8O5w3?DS`}OB=#IKL9N37`MGUx0ZCc` z8hN~M))M8+H*Jxo(F$lUeq{-5#itcdI-eE>0PRT_PW-08d> z_ajp#ZetC8vtG0oeFc0VqSy<~@+M^j)q_l30LoF>4!ufG^i;mMRDuDDn%cFb3>l3= zJwhMQjrDB|8PEcIP1654-d9b@%i2UYF8l)?Upd2FS)~I^puI*w7npWdm*6eWJ$?oa z4{aFeE93gyxAU?i@9k`ZDte+!V)@k7SN+J9@!M!H(C`d8toSeEz|OJ{+IsY<>4A(v zR8qtYWC3E()TWXTv>=S@sf}Pl%^+91d31jNAXq0LMrC)q0SO70i}^u+6EP8BsE7--z#{pjAPPq z!kjMRWqRk566Lr+#R&smjC|?@y-vI%%fZ5%g#eyMbzOUjI6&!FAu(3;6e*#HZf=1f(pMr+@hd1b{?LHJ{HLjS^7UT$;Hf zBVx55nUv(iro*TYZ%b<+Y7@6%!phnQK)W#@18D8KX#GLN9xPAG(@ozwsNkl+Ar#lC zoga!9&mF?D-M1|gFZO(V^yC|ES}Kgk(40Lh&%57g^LD+1dUz{iO=!K*nM(Q zq7M&MU@Kq-m9PFW4M8X82h*4^?8nBio>M9=-(IjBa6gv|D99m2CQYnB&>Kzt^G#>l zlMaM#q0fNDAP_e_^F98`fpzyxXl(_A*x`$k?zVDAS<>!|4ssXhM$bViNHBFGcfs{- zv^j)@>&0==_c-l?Jd6g1#-G67g&#eXWq)qUJZJy-FjDHK3)7OVQW*@yw^x=`_tt0*q6Fky| literal 0 HcmV?d00001 diff --git a/vripper-gui/src/main/resources/icons/32x32.png b/vripper-gui/src/main/resources/icons/32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..9d0f89e75d06560f1b9b111942a24d4c48b0ce63 GIT binary patch literal 948 zcmV;l155mgP)m1Na+O z6o?o6bvm9hc1g^Cs&M8o@Vz48dGQW2mLt|t3W^tN(KQv5Toga#E)wez#h(0s6hFZa z*u=WJaGfCdPBG|T;{(#f`YiUs5%XwP7WY(E5@w2v#e@cvg@uwIbBI2`PKx7J(-d#J zD0X=WCkleJ;WOh1h^|TRq_Fcnt3T=`&=~XY`WbfY3Sg9I};NnAF}K7 z5%0$6sN^xxs~5)(r=8Md78)B#Qq;`t+mdf2BZEi6VdC>OG)O)Z0li~ldsPGtCi3%1 zQq)3IljM6{UM|}BL_vY%6R5%oU~d-#goC9elK9KOfaF% z_|(}+k_T2-CC|61DdPFq-Y$6*cyR*TiU2|N^N!`@7;$Gp0uSx)llZH$GAWOOEu275 z5g@pdlcfTliV7n>T~tJ3xKnV9m`A};ToXVxMn*`H&s|+c3|~sK{F)|>w?Lo3nM;89 z>g!37pI)yKTOS-GvANn>X}ksc5}f%GBI9{^oJS%?k>gN^52vT=vB28epX%Tl&&`#_ zTj0em*ry06!L4Q1%hFOlX0ws+up5fwtbi8q2q#c22CTrXW!7SIvwq;1wcOe&jk5x3 zSK<<60s%3o0=Je~cuuTzca!g4l$1#0EWnzX`gs5!`V|HVXHpUm?d=)Gj<&b??)<#* ze{g5V@}LsIr8iE)*i8s8kdjfv?{pdkb(vnw^`RljZ$S|5h}Vq?py106g;8R@uFh1Q z>TOMp{Gs#%0SYHAi{^XHood` z5SHzB$){iwxF*&m!mG0uy}gQqFA6@`EwP?bV7v;vQ6xDp9?j}HZY5kN7-ZMt>=70O zq1)ZFT=RIOMc<2F?e8XnUWc^RE+Q@3F{`kD!{1q8ON$=m2!5W0=JF8YchD=2x6*Hg WxV%caL@D0@0000&&;yWZ}Q2)obNg3duC_P zoH--J49E~74LDFH#EZd+g| zQdF|3UcK6USXii}H7}3(ow&Vc533&q`=I{mz@_vp%`F9mtAG%#FrBIuE6mC9aZ{y8 zIBZrfU(V{N7-(ss)ukd66K2)QmCR2Vg4MwLG(Qdimj~3$cy;q8?bp_gojaMIQn_@g z<4Jhc(7^l-oQVmKiz!2MZS%0Gi1usix05HCpK@cz4qDAt$FXC~??5PK0cfo%sYyv- z%NE+NtrvB5%une&ae`K}71*?i`JI5w1@-_8k4xg=r%!3$QsaYzX4!%T%s-tMwY9XG z)bO)s%o~R$86JNS@J~cxaHQwbCECBOz~;@&AEj*md~_+}*#Q_HU~#KcD%d;NYBeCo_}zHORp=4?YIc0#|UloE#gY8KiozTw(qwc&|`t za&*+JT(*q))bd!-ttylOPVDUr`X3x{VuY4qJD#vY7u`YAUtas?Eq?eogvt z0Aq>*Erxr0X%?x+r%y9)V|F&RNrifPnAZcN2@Rn8;zgQA>Yv6&=6zC9LTysPvuByt z19AYqE+OR3zI`;06wWFYix$Or`?$V;e8QJ{rfm_b;T>bGvSeHDur zE2rJ?$cS07WC?561M)}gYZ{I6^7?g}OA2SHsHb83c3Ld;S9LXO&m%RyZe-JWaO4Qh zCG}@jmF?*~ew-FdwHFky_Pii(WY$J2E7YZK{d$^9YP7GMkucTMr$ghoy8LKiz=LpW6el9w}WP=-t5S zH2mfb&1k;v>@@$le3>3LCqf~!JSBy-Ib9+_tQ@FzvLG zEx0`>9i`o+kK`WgX3{6d1D%AKUA zmyMIkg-ACNn4Nsk2CSty5>kT@Zy8fBx|8{B$QRx4xsdihW5T^uxGqbK1n$}ju&pzO z(;YS$vKD|}0KXdko+q%*J=`XT&v$71vnm+Odk`YWS`gL(kBzC_4&1Uq@NYa;Yk6|8 d4sFMe{sU9M4Mme9Eqed}002ovPDHLkV1iunv@-wz literal 0 HcmV?d00001 diff --git a/vripper-gui/src/main/resources/icons/512x512.png b/vripper-gui/src/main/resources/icons/512x512.png new file mode 100644 index 0000000000000000000000000000000000000000..7f8b69f8bda1d6af6a6e833debe75ede0995ed1a GIT binary patch literal 18123 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 literal 0 HcmV?d00001 diff --git a/vripper-gui/src/main/resources/play.png b/vripper-gui/src/main/resources/play.png new file mode 100644 index 0000000000000000000000000000000000000000..055a1256975aa0bcb4ee847ddf507ebfc19dbe7d GIT binary patch literal 218 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTC&H|6fVg?3oVGw3ym^DWNDA?oa z;uumf=k2A9oQDlWTmyR-nS0J!qjC1}A>l}76^TEw5l=uK@R_3x!yHToV955uz={|iqE&aoRfnlcq?_B>=9Ywu* z_3~CSPA;SH3Mj3kQVPOfQTPOve&tR01cdkV@08Xbr}4w#aCj+dTcmUkgi%NoZxj(`cSud{M&@z+cT@{`6kWl9O7d#POf8=0aj7lV_W808y-1~}lJIsR^L9G=98#47C}lXo`|iDO z@xWu)s|LU+#HN#Z3t5&^LIXhI0}G8Vy}#o1pt=AwUkl3%0j7~SOaK&q=`sQmFu>cz zBamM_%9|`JbOZRZcmy1yavK03eCVG~p;u5Y6n?m-4hFBSq= z(J52_OydVmVGe}9hXw$`-<+#?7F|OFK&6eSs}~v+K7~EF;|wqxg;6j7N?TD;JsKxB zLb%j}(oyNUsnsf52aq@^WquAJywhUuzm9xm_fQbl<^ky4;M3qrRE4b2nipJ~>-oLP zio0iuyrU+j&7i8DKTlSxtY>$kpWX|$VBK`v4j6CaV*f!~wrxjE%T7m&9c8l9^f$Co z_=K%PvjDhkbsNjWmP}58gtYyM-7HC_>ze zE~_BKf)Mv3_N~Ksi3K6{Nm__=&j6zV#RPg0+cNz388FsuW1l+TWpzj2z=&>o6tt#P zX&OH=;<&5$uowQJl&Q{sY(!75#p@~TQ4X-ZOxe2j;bGT?HOdLLIyd4pD11|nuyxCz e*mVFv-CO|(4c!lvI6-E$sR$z z3=CCj3=9n|3=F@3LJcn%7)lKo7+xhXFj&oCU=S~uvn$XBD8ZKG?d}4kf#9d}?s_1_ zS>O>_45Sml_(QhSc_5?B)5S5w)#o3vHTMY%Y zH%c)bLM-B~Qp(Zh0wN&5n;(Cc^h zhx;>YJEyh%7W&%AJ!kvT_e@(@=ZVkZ_>*x71x%$jm-*IV*f8lc5zuD zFyWSp+E=+9#g=z|XS4v#&{x#Es(n%Aa@tmF3GS==+&>qIZDWw0V4! zdjuYc3EkuPFkL8)vtXryz`{le;VhTG4Zkk0EpzJrdhdk4&e@{f?lvA=YmaE$+%nB$ zb4>4a_1#Q%E)ESB8xFP1y0B7NFK=qm*^f~(g@1^$FkUlPI4`bUc+KGh(>eu?0(W&4 zXYm47#|CRFMaf+a+8j)Oavpr#Qrc_sbIv!#NAFn}7#wyy;A2!dnEBw~wA(;8GkCiC KxvXFyfeBYe!ob#VwQPP&Sv<=Jh^Yf=5;wnTuhltGp@D%`}G5^YIhvqpPs6aCR!bd>p)Cl5zAlnGL}~gSWjQNXF47>lq>mP1B}%0-kjW0Btt- z;!-j$v<)4&+b^_{UR)|h<2mP-bOM~P;+N+aCj+$YL72|8MCkhK9tyngbH3)a?GMAr zC3W#7p|M7c#2)FTQmi(XY+NA9?}q5|t0>*+=o!$jv`4A@P7)GTjUs`cQl-RozW?F+ z#@z{;uecwk!23S%x86qS@YCP4>0X%D211teHLvl1e2@QKlqw#$qR|i}eZwx1#7Su# zR%}vk>6A2usqsVK*R5`iSik?KcmA)sEn?lvgo^^MDAcxyx$sL$$Bu0eVs(CN0w@M? z>1BkDJ&Ta(0+iGPAv*p%LRZ@p37GznqfY=pt_%X_Z245yjlAmo!}a}FL;`1I5NNiO zx2DB&0!GaXM~5CU0RZ@B6S(%#a{^|4r%2$7j0i;R1m?Ys*92~MixT=RnZOV43EcQD zmw-b;^_|}5Kz)aF3B{AR*83zjWhXI@fi{5@mr+DKJz@ep!`gkIY>W2<);SuPI${DP zB_)%%t;c25>Ye^;om&Zhw<5!M0Jo!1zJpBvFb0@^xob0rh-amM7bdZ2d^_DFUpiF* z5j>af8PW*ENE~pCk`kqqWT4rvcrj9PS+k{7T>96kY-qY~b&Qk%@UE<^Y@BogF%r`d z@slGGRgJ?N>ixk!+31G*h@}@13l*=7k3RVH!12=c+sHWIJfv!)m+u_t(5u%|5{Nhz z03Nwt%zc6z5pRjbgDsPm#9#0@)MI!gG zGFB`jO=6T-MmiFs#4>&ANq1r|$XfA98B3NS$Z397_GeXkE|t literal 0 HcmV?d00001 diff --git a/vripper-gui/src/main/resources/settings.png b/vripper-gui/src/main/resources/settings.png new file mode 100644 index 0000000000000000000000000000000000000000..bcbedf4296cfdab54e0cdeeb6c4df64eef0c68eb GIT binary patch literal 1279 zcmV`Bb4Vz3bgUrp7>gT3z4@|k09-+X#SL8HQSAUEoMLeUm++Qu07Bsf7B>h9ALs`F z-&{vxEsIOUoZWiV?>E^RKExGVHrRnbxN7b7^=IRKLc$wD!g4Zs`liU_^VP=FLcv-m zZ*f&fT;bLBzV7$A6K~%`E6)}DV?e&U3u|2AC;1vY-1S$q+mCRC?+FE0J-;nSr0?9o zC0y)Zg!m=3LLu=dbK+Zk{UsDiuHcNjNLvg3_bIz|uQ7d5M{Y_c_xh5Ptkj$_tKy7` zvNN2Sa*aN}XjPo#(VF|42)p&Z5&6l5<{ez(54tbNR~ydw?1D3#v7AIY+zw68Ir*x7 zAY9^VLE$sn0nkCC>wIEv(HYK8YZ3PvJu@-4XtEQRY}>vulzgX<;BOtLloE+_$(a0x zm7K$sn*L7Iu^1YFP;g#%|JKCyI{FZsM|JfT~xQPRfGMzT}d#myBQ z_YL64k>y*2g0I7zjL*z(8~Mb#kxz{Pe#_hNWMB%}wtX{KxF{6->%00P-R5AbcxEJH z?N4O0yIf089!R9Dvw=mOv7B5$XW|Mz%U9d?Go$%l;`dt$w^9osU#j@|Or)&Wf}TCx zr=Y2!aEWttu(C+EEQ#m54(UJ&TKO=a2UEorzvstFulDqRxWa9CiVcFiguK`3D4z`o zVCvcM07^rC&yVu*<{glDnhgpralz}9I)-l=WV5@%{0$|`w+D42u_=Y%i-7|W3g*2| zX}0krK;**wy^zW5_ATV>bngi9`M?42I_5T~P&uH8Cl7@Adp=Wq&NqcbPYNmcXdv=5 z#&QWCd!5px=^H@GIveIQkt&|^En=(E+l9W%$;M-m$P0yI{pS6!HcGA1pS-nT+(sq* zDI}z|iD&HP-86p(juGJp*@~Mg>j#oRIKg@>u(}K=P9{e4yXqbzMe@ zS*=mx5{*0BJ>SphG7{KM4Q(Vl9XLjKtzhHhz5#ICzLg3muH&>&a6YsKxNMEW<6=q>AajU?Y$vH?6v|3hfLTeWpp1(j|(l!t0o0rl7kgm+}^(8OiVx+qOfh(Nxyyz;)e%?YNS+Fx|bmT{)RizY_c(Q1> z@4oiFYI~n6UtUw_V2$KW78N9{as}_w6_W&4|3F)NNw75_f0^4$5Bm8dA(;eaEg5CI zK$5xk4oE!7;s&Acg7ywbc$38qT*4`X8hC}p4ctIa6?I6mxS>G*7ijN5b3cn4glz7i pKe5C9=%#*8a?StEcL!{n@E>Lm;ibv7m>K{8002ovPDHLkV1j76Zgl_v literal 0 HcmV?d00001 diff --git a/vripper-gui/src/main/resources/stop.png b/vripper-gui/src/main/resources/stop.png new file mode 100644 index 0000000000000000000000000000000000000000..f6c8a5e851bb601b1be1b8fd73db196e90eb156c GIT binary patch literal 147 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTC&H|6fVg?3oVGw3ym^DWNDCp|x z;uumf=j|m%&I1kt%m*d9>m%cNRa=)EFsnZ}p~k@Q!Fsu6e4NdPNvv86ds{HDA_K#T aSbnb3nz)iXjEq1789ZJ6T-G@yGywqK*(j(0 literal 0 HcmV?d00001 diff --git a/vripper-gui/src/main/resources/trash.png b/vripper-gui/src/main/resources/trash.png new file mode 100644 index 0000000000000000000000000000000000000000..b5dc52e46011a426bb96ffdbec77c25324aa2f86 GIT binary patch literal 376 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA1|-9oezpTC&H|6fVg?3oVGw3ym^DX&fq_xW z)5S5QV$R#?hIxk!BwB0x*NK*E7Qf`&;h_CL(7jD*qj{M4nLYt;r*b}l)pI$8*SIzb zOQ?p5i7-$4EG@X#zv|S7l$Z72PJiHLWMEk9I8jo==-Zs6nY~B$@~zJkRhqkIcjvk7 zE6?R}s9N1utNME-qG7_}KljV0 z-1fgq*M45HUE}E|<>NOK7`q-mDUPa(y{9@qYfl2>Dn{&^w&E3XE{Y{O+_n U!5NPEz@TLCboFyt=akR{0O>@Vz5oCK literal 0 HcmV?d00001 diff --git a/vripper-server/pom.xml b/vripper-server/pom.xml deleted file mode 100644 index 4a9b7112..00000000 --- a/vripper-server/pom.xml +++ /dev/null @@ -1,160 +0,0 @@ - - - 4.0.0 - - tn.mnlr - vripper - 3.5.4 - - vripper-server - vripper-server - vripper-server - - - 11 - - - - - org.springframework.boot - spring-boot-starter-websocket - - - org.springframework.boot - spring-boot-starter-actuator - - - org.springframework.boot - spring-boot-starter-data-jdbc - - - org.liquibase - liquibase-core - - - org.hsqldb - hsqldb - runtime - - - org.projectlombok - lombok - true - - - org.springframework.boot - spring-boot-starter-test - test - - - org.springframework.restdocs - spring-restdocs-mockmvc - test - - - org.apache.httpcomponents - httpclient - - - net.sourceforge.htmlcleaner - htmlcleaner - 2.24 - - - io.projectreactor - reactor-core - - - net.jodah - failsafe - 2.0.1 - - - com.github.ben-manes.caffeine - caffeine - - - tn.mnlr - vripper-ui - ${project.version} - runtime - ${buildClassifier} - - - - - - - maven-jar-plugin - - ${buildClassifier} - - - ${maven.build.timestamp} - ${user.name} - - - - - - org.springframework.boot - spring-boot-maven-plugin - - - repackage - - repackage - - - ${buildClassifier} - - - - - - - - - - web - - true - - - web - - - - - maven-resources-plugin - - - copy-resources - validate - - copy-resources - - - ${project.build.directory}/classes/static - - - ${project.parent.basedir}/vripper-ui/dist/vripper-ui/ - - - - - - - - - - - - electron - - electron - - - - diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/EventListenerBean.java b/vripper-server/src/main/java/tn/mnlr/vripper/EventListenerBean.java deleted file mode 100644 index 423d58a6..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/EventListenerBean.java +++ /dev/null @@ -1,31 +0,0 @@ -package tn.mnlr.vripper; - -import lombok.Getter; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.event.ContextRefreshedEvent; -import org.springframework.context.event.EventListener; -import org.springframework.stereotype.Component; -import tn.mnlr.vripper.jpa.repositories.IPostRepository; -import tn.mnlr.vripper.services.DataService; - -@Component -public class EventListenerBean { - - @Getter private static boolean init = false; - - private final IPostRepository postRepository; - private final DataService dataService; - - @Autowired - public EventListenerBean(IPostRepository postRepository, DataService dataService) { - this.postRepository = postRepository; - this.dataService = dataService; - } - - @EventListener - public void onApplicationEvent(ContextRefreshedEvent event) { - postRepository.setDownloadingToStopped(); - dataService.sortPostsByRank(); - init = true; - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/SpringContext.java b/vripper-server/src/main/java/tn/mnlr/vripper/SpringContext.java deleted file mode 100644 index 90034f80..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/SpringContext.java +++ /dev/null @@ -1,39 +0,0 @@ -package tn.mnlr.vripper; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.BeansException; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.context.ConfigurableApplicationContext; -import org.springframework.stereotype.Component; - -import java.util.Map; - -@Component -@Slf4j -public class SpringContext implements ApplicationContextAware { - - private static ConfigurableApplicationContext context; - - public static T getBean(Class beanClass) { - return context.getBean(beanClass); - } - - public static Map getBeansOfType(Class beanClass) { - return context.getBeansOfType(beanClass); - } - - public static void close() { - log.info("Application terminating..."); - if (context != null) { - context.close(); - } - } - - @Override - public void setApplicationContext(ApplicationContext context) throws BeansException { - - // store ApplicationContext reference to access required beans later on - SpringContext.context = ((ConfigurableApplicationContext) context); - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/Utils.java b/vripper-server/src/main/java/tn/mnlr/vripper/Utils.java deleted file mode 100644 index b9ea31ef..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/Utils.java +++ /dev/null @@ -1,24 +0,0 @@ -package tn.mnlr.vripper; - -import lombok.extern.slf4j.Slf4j; - -import java.io.IOException; -import java.io.PrintWriter; -import java.io.StringWriter; - -@Slf4j -public class Utils { - - public static String throwableToString(Throwable th) { - try (StringWriter stringWriter = new StringWriter(); - PrintWriter printWriter = new PrintWriter(stringWriter)) { - th.printStackTrace(printWriter); - printWriter.flush(); - stringWriter.flush(); - return stringWriter.toString(); - } catch (IOException e) { - log.error("Failed to convert throwable to string", e); - } - return "Cannot display error, please check the log file for details"; - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/VripperApplication.java b/vripper-server/src/main/java/tn/mnlr/vripper/VripperApplication.java deleted file mode 100644 index a1001823..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/VripperApplication.java +++ /dev/null @@ -1,19 +0,0 @@ -package tn.mnlr.vripper; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; - -@SpringBootApplication -@Slf4j -public class VripperApplication { - - public static void main(String[] args) { - try { - Runtime.getRuntime().addShutdownHook(new Thread(SpringContext::close)); - SpringApplication.run(VripperApplication.class, args); - } catch (Exception e) { - log.error("Failed to run the application", e); - } - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/download/DownloadJob.java b/vripper-server/src/main/java/tn/mnlr/vripper/download/DownloadJob.java deleted file mode 100644 index d08e1bac..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/download/DownloadJob.java +++ /dev/null @@ -1,295 +0,0 @@ -package tn.mnlr.vripper.download; - -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; -import net.jodah.failsafe.function.CheckedRunnable; -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.AbstractExecutionAwareRequest; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.protocol.HttpClientContext; -import org.apache.http.impl.client.BasicCookieStore; -import org.apache.http.util.EntityUtils; -import tn.mnlr.vripper.SpringContext; -import tn.mnlr.vripper.exception.DownloadException; -import tn.mnlr.vripper.exception.HostException; -import tn.mnlr.vripper.jpa.domain.Image; -import tn.mnlr.vripper.jpa.domain.Post; -import tn.mnlr.vripper.jpa.domain.enums.Status; -import tn.mnlr.vripper.services.*; -import tn.mnlr.vripper.services.domain.Settings; - -import javax.imageio.ImageIO; -import javax.imageio.ImageReader; -import javax.imageio.stream.ImageInputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.StandardCopyOption; -import java.util.*; - -@Slf4j -public class DownloadJob implements CheckedRunnable { - - private static final Object LOCK = new Object(); - private static final int READ_BUFFER_SIZE = 8192; - private final DataService dataService; - private final PathService pathService; - private final ConnectionService cm; - private final VGAuthService authService; - private final DownloadSpeedService downloadSpeedService; - private final Settings settings; - private final HttpClientContext context; - @Getter private final Image image; - @Getter private final Post post; - private volatile boolean stopped = false; - @Getter private volatile boolean finished = false; - - DownloadJob(Post post, Image image, Settings settings) { - this.image = image; - this.post = post; - this.settings = settings; - dataService = SpringContext.getBean(DataService.class); - pathService = SpringContext.getBean(PathService.class); - cm = SpringContext.getBean(ConnectionService.class); - authService = SpringContext.getBean(VGAuthService.class); - downloadSpeedService = SpringContext.getBean(DownloadSpeedService.class); - context = HttpClientContext.create(); - context.setCookieStore(new BasicCookieStore()); - context.setAttribute( - ContextAttributes.OPEN_CONNECTION.toString(), - Collections.synchronizedList(new ArrayList())); - } - - public void download(final Post post, final Image image) throws DownloadException { - - try { - - image.setStatus(Status.DOWNLOADING); - image.setCurrent(0); - dataService.updateImageStatus(image.getStatus(), image.getId()); - dataService.updateImageCurrent(image.getCurrent(), image.getId()); - - synchronized (LOCK) { - if (!post.getStatus().equals(Status.DOWNLOADING) - && !post.getStatus().equals(Status.PARTIAL)) { - post.setStatus(Status.DOWNLOADING); - dataService.updatePostStatus(post.getStatus(), post.getId()); - } - - // The post may be updated and the download directory might be set by another thread - Post updatedPost = dataService.findById(post.getId()).orElseThrow(); - if (updatedPost.getDownloadDirectory() == null) { - pathService.createDefaultPostFolder(updatedPost, settings); - } - if (settings.getLeaveThanksOnStart() != null && settings.getLeaveThanksOnStart()) { - authService.leaveThanks(updatedPost); - } - } - - if (stopped) { - return; - } - /* - * HOST SPECIFIC - */ - log.debug( - String.format( - "Getting image url and name from %s using %s", image.getUrl(), image.getHost())); - HostService.NameUrl nameAndUrl = image.getHost().getNameAndUrl(image.getUrl(), context); - log.debug(String.format("Resolved name for %s: %s", image.getUrl(), nameAndUrl.getName())); - log.debug( - String.format("Resolved image url for %s: %s", image.getUrl(), nameAndUrl.getUrl())); - /* - * END HOST SPECIFIC - */ - - if (stopped) { - return; - } - String formatImageFileName = pathService.formatImageFileName(nameAndUrl.getName()); - log.debug( - String.format( - "Sanitizing image name from %s to %s", nameAndUrl.getName(), formatImageFileName)); - nameAndUrl = new HostService.NameUrl(formatImageFileName, nameAndUrl.getUrl()); - - HttpClient client = cm.getClient().build(); - - log.debug(String.format("Downloading %s", nameAndUrl.getUrl())); - HttpGet httpGet = cm.buildHttpGet(nameAndUrl.getUrl(), context); - httpGet.addHeader("Referer", image.getUrl()); - try (CloseableHttpResponse response = - (CloseableHttpResponse) client.execute(httpGet, context)) { - - if (response.getStatusLine().getStatusCode() / 100 != 2) { - EntityUtils.consumeQuietly(response.getEntity()); - throw new DownloadException( - String.format("Server returned code %d", response.getStatusLine().getStatusCode())); - } - if (stopped) { - return; - } - File outputFile = Files.createTempFile("vripper", "tmp").toFile(); - try (InputStream downloadStream = response.getEntity().getContent(); - FileOutputStream fos = new FileOutputStream(outputFile)) { - - if (stopped) { - return; - } - - image.setTotal(response.getEntity().getContentLength()); - dataService.updateImageTotal(image.getTotal(), image.getId()); - - log.debug(String.format("%s length is %d", nameAndUrl.getUrl(), image.getTotal())); - log.debug(String.format("Starting data transfer for %s", nameAndUrl.getUrl())); - - byte[] buffer = new byte[READ_BUFFER_SIZE]; - int read; - while ((read = downloadStream.read(buffer, 0, READ_BUFFER_SIZE)) != -1 && !stopped) { - fos.write(buffer, 0, read); - image.increase(read); - downloadSpeedService.increase(read); - dataService.updateImageCurrent(image.getCurrent(), image.getId()); - } - fos.flush(); - EntityUtils.consumeQuietly(response.getEntity()); - if (stopped) { - return; - } - } - checkImageTypeAndRename( - dataService.findById(post.getId()).orElseThrow(), - outputFile, - nameAndUrl.getName(), - image.getIndex()); - } - } catch (Exception e) { - if (stopped) { - return; - } - throw new DownloadException(e); - } finally { - if (image.getCurrent() == image.getTotal() && image.getTotal() > 0) { - image.setStatus(Status.COMPLETE); - } else if (stopped) { - image.setStatus(Status.STOPPED); - } else { - image.setStatus(Status.ERROR); - } - dataService.updateImageStatus(image.getStatus(), image.getId()); - finished = true; - } - } - - private void checkImageTypeAndRename(Post post, File outputFile, String imageName, int index) - throws HostException { - try (ImageInputStream iis = ImageIO.createImageInputStream(outputFile)) { - Iterator it = ImageIO.getImageReaders(iis); - if (!it.hasNext()) { - throw new HostException("Image file is not recognized!"); - } - ImageReader reader = it.next(); - if (reader.getFormatName().equalsIgnoreCase("JPEG")) { - String imageNameLC = imageName.toLowerCase(); - if (!imageNameLC.endsWith("_jpg") && !imageNameLC.endsWith("_jpeg")) { - imageName += ".jpg"; - } else { - String toReplace = null; - if (imageNameLC.endsWith("_jpg")) { - toReplace = "_jpg"; - } else if (imageNameLC.endsWith("_jpeg")) { - toReplace = "_jpeg"; - } - if (toReplace != null) { - imageName = imageName.substring(0, imageName.length() - toReplace.length()) + ".jpg"; - } - } - } else if (reader.getFormatName().equalsIgnoreCase("PNG")) { - String imageNameLC = imageName.toLowerCase(); - if (!imageNameLC.endsWith("_png")) { - imageName += ".png"; - } else { - String toReplace = null; - if (imageNameLC.endsWith("_png")) { - toReplace = "_png"; - } - if (toReplace != null) { - imageName = imageName.substring(0, imageName.length() - toReplace.length()) + ".png"; - } - } - } - } catch (Exception e) { - throw new HostException("Failed to guess image format", e); - } - try { - pathService.getDirectoryAccess().lock(); - File downloadDestinationFolder = pathService.calcDownloadDirectory(post, settings); - File outImage = - new File( - downloadDestinationFolder, - (settings.getForceOrder() ? String.format("%03d_", index) : "") + imageName); - Files.copy(outputFile.toPath(), outImage.toPath(), StandardCopyOption.REPLACE_EXISTING); - } catch (Exception e) { - throw new HostException("Failed to rename the image", e); - } finally { - pathService.getDirectoryAccess().unlock(); - try { - Files.delete(outputFile.toPath()); - } catch (IOException e) { - log.warn(String.format("Failed to delete temporary file %s", outputFile.getAbsolutePath())); - } - } - } - - @Override - public void run() throws Exception { - if (stopped) { - finished = true; - return; - } - log.debug(String.format("Starting downloading %s", image.getUrl())); - download(post, image); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - DownloadJob that = (DownloadJob) o; - return Objects.equals(image, that.image) && Objects.equals(post, that.post); - } - - @Override - public int hashCode() { - return Objects.hash(image, post); - } - - public void stop() { - this.stopped = true; - List requests = - (List) - this.context.getAttribute(ContextAttributes.OPEN_CONNECTION.toString()); - if (requests != null) { - for (AbstractExecutionAwareRequest request : requests) { - request.abort(); - } - } - } - - public enum ContextAttributes { - OPEN_CONNECTION("OPEN_CONNECTION"); - - private final String value; - - ContextAttributes(String value) { - this.value = value; - } - - @Override - public String toString() { - return value; - } - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/download/DownloadJobWrapper.java b/vripper-server/src/main/java/tn/mnlr/vripper/download/DownloadJobWrapper.java deleted file mode 100644 index 89e03cc9..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/download/DownloadJobWrapper.java +++ /dev/null @@ -1,70 +0,0 @@ -package tn.mnlr.vripper.download; - -import lombok.extern.slf4j.Slf4j; -import net.jodah.failsafe.Failsafe; -import tn.mnlr.vripper.SpringContext; -import tn.mnlr.vripper.Utils; -import tn.mnlr.vripper.jpa.domain.LogEvent; -import tn.mnlr.vripper.jpa.domain.enums.Status; -import tn.mnlr.vripper.jpa.repositories.ILogEventRepository; -import tn.mnlr.vripper.services.ConnectionService; -import tn.mnlr.vripper.services.DataService; - -import java.time.LocalDateTime; - -@Slf4j -public class DownloadJobWrapper implements Runnable { - - private final DownloadService downloadService; - private final DataService dataService; - private final ConnectionService connectionService; - private final ILogEventRepository eventRepository; - - private final DownloadJob downloadJob; - - public DownloadJobWrapper(final DownloadJob downloadJob) { - downloadService = SpringContext.getBean(DownloadService.class); - dataService = SpringContext.getBean(DataService.class); - connectionService = SpringContext.getBean(ConnectionService.class); - eventRepository = SpringContext.getBean(ILogEventRepository.class); - - this.downloadJob = downloadJob; - } - - @Override - public void run() { - Failsafe.with(connectionService.getRetryPolicy()) - .onFailure( - e -> { - try { - LogEvent logEvent = - new LogEvent( - LogEvent.Type.DOWNLOAD, - LogEvent.Status.ERROR, - LocalDateTime.now(), - String.format( - "Failed to download %s\n %s", - downloadJob.getImage().getUrl(), - Utils.throwableToString(e.getFailure()))); - eventRepository.save(logEvent); - } catch (Exception exp) { - log.error("Failed to save event", exp); - } - log.error( - String.format( - "Failed to download %s after %d tries", - downloadJob.getImage().getUrl(), e.getAttemptCount()), - e.getFailure()); - downloadJob.getImage().setStatus(Status.ERROR); - dataService.updateImageStatus( - downloadJob.getImage().getStatus(), downloadJob.getImage().getId()); - }) - .onComplete( - e -> { - dataService.afterJobFinish(downloadJob.getImage(), downloadJob.getPost()); - downloadService.afterJobFinish(downloadJob); - log.debug(String.format("Finished downloading %s", downloadJob.getImage().getUrl())); - }) - .run(downloadJob); - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/download/DownloadService.java b/vripper-server/src/main/java/tn/mnlr/vripper/download/DownloadService.java deleted file mode 100644 index 4af11f76..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/download/DownloadService.java +++ /dev/null @@ -1,274 +0,0 @@ -package tn.mnlr.vripper.download; - -import lombok.NonNull; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import tn.mnlr.vripper.host.Host; -import tn.mnlr.vripper.jpa.domain.Image; -import tn.mnlr.vripper.jpa.domain.Post; -import tn.mnlr.vripper.jpa.domain.enums.Status; -import tn.mnlr.vripper.services.DataService; -import tn.mnlr.vripper.services.MetadataService; -import tn.mnlr.vripper.services.SettingsService; -import tn.mnlr.vripper.services.domain.Settings; - -import javax.annotation.PostConstruct; -import java.util.*; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.stream.Collectors; - -@Service -@Slf4j -public class DownloadService { - - private final int MAX_POOL_SIZE = 12; - - private final SettingsService settingsService; - private final DataService dataService; - private final MetadataService metadataService; - private final List hosts; - - private final Map threadCount = new HashMap<>(); - private final ExecutorService executor = Executors.newFixedThreadPool(MAX_POOL_SIZE); - private final List running = new ArrayList<>(); - private final List pending = new ArrayList<>(); - - private final Thread pollThread; - - @Autowired - public DownloadService( - SettingsService settingsService, - DataService dataService, - MetadataService metadataService, - List hosts) { - this.settingsService = settingsService; - this.dataService = dataService; - this.metadataService = metadataService; - this.hosts = hosts; - pollThread = - new Thread( - () -> { - while (!Thread.interrupted()) { - try { - synchronized (this) { - List accepted = new ArrayList<>(); - List candidates = getCandidates(candidateCount()); - candidates.forEach( - c -> { - if (canRun(c.getImage().getHost())) { - accepted.add(c); - } - }); - pending.removeAll(accepted); - accepted.forEach(this::schedule); - accepted.clear(); - this.wait(); - } - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - break; - } - } - }, - "Download scheduler thread"); - } - - @PostConstruct - private void init() { - pollThread.start(); - } - - public void destroy() throws Exception { - log.info("Shutting down ExecutionService"); - pollThread.interrupt(); - executor.shutdown(); - dataService - .findAllPosts() - .forEach( - p -> { - log.debug(String.format("Stopping download jobs for %s", p)); - this.stopRunning(p.getPostId()); - }); - if (!executor.awaitTermination(5, TimeUnit.SECONDS)) { - log.warn("Some jobs are still running!, forcing shutdown"); - executor.shutdownNow(); - } - } - - private synchronized void stopRunning(@NonNull String postId) { - List stopping = new ArrayList<>(); - Iterator iterator = running.iterator(); - while (iterator.hasNext()) { - DownloadJob downloadJob = iterator.next(); - if (postId.equals(downloadJob.getPost().getPostId())) { - downloadJob.stop(); - iterator.remove(); - stopping.add(downloadJob); - } - } - - while (!stopping.isEmpty()) { - stopping.removeIf(DownloadJob::isFinished); - try { - Thread.sleep(100); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - } - - public void stopAll(List postIds) { - stop( - Objects.requireNonNullElseGet( - postIds, - () -> - dataService.findAllPosts().stream() - .map(Post::getPostId) - .collect(Collectors.toList()))); - } - - public void restartAll(List posIds) { - restart( - Objects.requireNonNullElseGet( - posIds, - () -> - dataService.findAllPosts().stream() - .map(Post::getPostId) - .collect(Collectors.toList()))); - } - - private void restart(@NonNull List postIds) { - Map> data = new HashMap<>(); - for (String postId : postIds) { - if (isPending(postId)) { - log.warn( - String.format("Cannot restart, jobs are currently running for post id %s", postIds)); - continue; - } - List images = dataService.findByPostIdAndIsNotCompleted(postId); - if (images.isEmpty()) { - continue; - } - Post post = dataService.findPostByPostId(postId).orElseThrow(); - log.debug(String.format("Restarting %d jobs for post id %s", images.size(), postIds)); - data.put(post, images); - } - enqueue(data); - } - - private synchronized boolean isPending(String postId) { - return pending.stream().anyMatch(p -> p.getPost().getPostId().equals(postId)); - } - - private synchronized boolean isRunning(String postId) { - return running.stream().anyMatch(p -> p.getPost().getPostId().equals(postId)); - } - - private synchronized void stop(List postIds) { - - for (String postId : postIds) { - final Post post = dataService.findPostByPostId(postId).orElseThrow(); - if (post == null) { - continue; - } - pending.removeIf(p -> p.getPost().equals(post)); - stopRunning(postId); - dataService.stopImagesByPostIdAndIsNotCompleted(postId); - dataService.finishPost(post); - } - metadataService.stopFetchingMetadata(postIds); - } - - private boolean canRun(Host host) { - boolean canRun; - int totalRunning = threadCount.values().stream().mapToInt(AtomicInteger::get).sum(); - canRun = - threadCount.get(host).get() < settingsService.getSettings().getMaxThreads() - && (settingsService.getSettings().getMaxTotalThreads() == 0 - ? totalRunning < MAX_POOL_SIZE - : totalRunning < settingsService.getSettings().getMaxTotalThreads()); - if (canRun) { - threadCount.get(host).incrementAndGet(); - return true; - } - return false; - } - - private Map candidateCount() { - HashMap map = new HashMap<>(); - hosts.forEach( - h -> { - AtomicInteger count = threadCount.get(h); - if (count == null) { - count = new AtomicInteger(0); - threadCount.put(h, count); - } - map.put(h, settingsService.getSettings().getMaxThreads() - count.get()); - }); - return map; - } - - private List getCandidates(Map candidateCount) { - List candidates = new ArrayList<>(); - for (DownloadJob downloadJob : pending) { - Host host = downloadJob.getImage().getHost(); - Integer maxPerHost = candidateCount.get(host); - if (maxPerHost > 0) { - candidates.add(downloadJob); - candidateCount.put(host, maxPerHost - 1); - } - } - return candidates; - } - - public void enqueue(Map> images) { - synchronized (this) { - for (Map.Entry> entry : images.entrySet()) { - entry.getKey().setStatus(Status.PENDING); - dataService.updatePostStatus(entry.getKey().getStatus(), entry.getKey().getId()); - for (Image image : entry.getValue()) { - log.debug(String.format("Enqueuing a job for %s", image.getUrl())); - image.init(); - dataService.updateImageStatus(image.getStatus(), image.getId()); - dataService.updateImageCurrent(image.getCurrent(), image.getId()); - DownloadJob downloadJob = - new DownloadJob( - entry.getKey(), image, (Settings) settingsService.getSettings().clone()); - pending.add(downloadJob); - } - } - pending.sort(Comparator.comparing(e -> e.getPost().getAddedOn())); - this.notify(); - } - } - - private synchronized void schedule(DownloadJob downloadJob) { - log.debug(String.format("Scheduling a job for %s", downloadJob.getImage().getUrl())); - executor.execute(new DownloadJobWrapper(downloadJob)); - running.add(downloadJob); - } - - public void afterJobFinish(DownloadJob downloadJob) { - synchronized (this) { - running.remove(downloadJob); - threadCount.get(downloadJob.getImage().getHost()).decrementAndGet(); - if (!isPending(downloadJob.getPost().getPostId()) - && !isRunning(downloadJob.getPost().getPostId())) { - dataService.finishPost(downloadJob.getPost()); - } - this.notify(); - } - } - - public int pendingCount() { - return pending.size(); - } - - public int runningCount() { - return running.size(); - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/event/Event.java b/vripper-server/src/main/java/tn/mnlr/vripper/event/Event.java deleted file mode 100644 index 985324d3..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/event/Event.java +++ /dev/null @@ -1,34 +0,0 @@ -package tn.mnlr.vripper.event; - -import lombok.Getter; - -@Getter -public class Event { - - private final T data; - private final Kind kind; - - private Event(Kind kind, T data) { - this.data = data; - this.kind = kind; - } - - public static Event wrap(Kind kind, T data) { - return new Event<>(kind, data); - } - - public enum Kind { - POST_UPDATE, - POST_REMOVE, - IMAGE_UPDATE, - METADATA_UPDATE, - QUEUED_UPDATE, - QUEUED_REMOVE, - LOG_EVENT_UPDATE, - LOG_EVENT_REMOVE, - VG_USER, - GLOBAL_STATE, - BYTES_PER_SECOND, - SETTINGS_UPDATE - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/event/EventBus.java b/vripper-server/src/main/java/tn/mnlr/vripper/event/EventBus.java deleted file mode 100644 index 0f1634dd..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/event/EventBus.java +++ /dev/null @@ -1,27 +0,0 @@ -package tn.mnlr.vripper.event; - -import org.springframework.stereotype.Service; -import reactor.core.publisher.Flux; -import reactor.core.publisher.Sinks; - -import javax.annotation.PreDestroy; - -@Service -public class EventBus { - - public static final Sinks.EmitFailureHandler RETRY = (signalType, emitResult) -> true; - private final Sinks.Many> sink = Sinks.many().multicast().onBackpressureBuffer(); - - public void publishEvent(Event event) { - sink.emitNext(event, RETRY); - } - - public Flux> flux() { - return sink.asFlux(); - } - - @PreDestroy - private void destroy() { - sink.emitComplete(RETRY); - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/exception/DownloadException.java b/vripper-server/src/main/java/tn/mnlr/vripper/exception/DownloadException.java deleted file mode 100644 index 49c65246..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/exception/DownloadException.java +++ /dev/null @@ -1,12 +0,0 @@ -package tn.mnlr.vripper.exception; - -public class DownloadException extends Exception { - - public DownloadException(String message) { - super(message); - } - - public DownloadException(Throwable e) { - super(e); - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/exception/HostException.java b/vripper-server/src/main/java/tn/mnlr/vripper/exception/HostException.java deleted file mode 100644 index 0a53a8a8..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/exception/HostException.java +++ /dev/null @@ -1,15 +0,0 @@ -package tn.mnlr.vripper.exception; - -public class HostException extends Exception { - public HostException(Throwable e) { - super(e); - } - - public HostException(String message) { - super(message); - } - - public HostException(String message, Throwable e) { - super(message, e); - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/exception/HtmlProcessorException.java b/vripper-server/src/main/java/tn/mnlr/vripper/exception/HtmlProcessorException.java deleted file mode 100644 index 61bebe07..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/exception/HtmlProcessorException.java +++ /dev/null @@ -1,8 +0,0 @@ -package tn.mnlr.vripper.exception; - -public class HtmlProcessorException extends Exception { - - public HtmlProcessorException(Throwable e) { - super(e); - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/exception/PostParseException.java b/vripper-server/src/main/java/tn/mnlr/vripper/exception/PostParseException.java deleted file mode 100644 index dcd6bede..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/exception/PostParseException.java +++ /dev/null @@ -1,16 +0,0 @@ -package tn.mnlr.vripper.exception; - -public class PostParseException extends Exception { - - public PostParseException(String message) { - super(message); - } - - public PostParseException(String message, Throwable e) { - super(message, e); - } - - public PostParseException(Throwable e) { - super(e); - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/exception/QueueException.java b/vripper-server/src/main/java/tn/mnlr/vripper/exception/QueueException.java deleted file mode 100644 index 7d545bdb..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/exception/QueueException.java +++ /dev/null @@ -1,16 +0,0 @@ -package tn.mnlr.vripper.exception; - -public class QueueException extends Exception { - - public QueueException(String message) { - super(message); - } - - public QueueException(String message, Throwable e) { - super(message, e); - } - - public QueueException(Throwable e) { - super(e); - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/exception/RenameException.java b/vripper-server/src/main/java/tn/mnlr/vripper/exception/RenameException.java deleted file mode 100644 index 3d8a2687..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/exception/RenameException.java +++ /dev/null @@ -1,10 +0,0 @@ -package tn.mnlr.vripper.exception; - -import java.io.IOException; - -public class RenameException extends Exception { - - public RenameException(String message, IOException e) { - super(message, e); - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/exception/ValidationException.java b/vripper-server/src/main/java/tn/mnlr/vripper/exception/ValidationException.java deleted file mode 100644 index 5d3d3ae5..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/exception/ValidationException.java +++ /dev/null @@ -1,8 +0,0 @@ -package tn.mnlr.vripper.exception; - -public class ValidationException extends Exception { - - public ValidationException(String message) { - super(message); - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/exception/VripperException.java b/vripper-server/src/main/java/tn/mnlr/vripper/exception/VripperException.java deleted file mode 100644 index 74a14e9c..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/exception/VripperException.java +++ /dev/null @@ -1,11 +0,0 @@ -package tn.mnlr.vripper.exception; - -public class VripperException extends Exception { - public VripperException(String message) { - super(message); - } - - public VripperException(Throwable e) { - super(e); - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/exception/XpathException.java b/vripper-server/src/main/java/tn/mnlr/vripper/exception/XpathException.java deleted file mode 100644 index cf359d77..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/exception/XpathException.java +++ /dev/null @@ -1,7 +0,0 @@ -package tn.mnlr.vripper.exception; - -public class XpathException extends Exception { - public XpathException(Throwable e) { - super(e); - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/host/AcidimgHost.java b/vripper-server/src/main/java/tn/mnlr/vripper/host/AcidimgHost.java deleted file mode 100644 index a8bf628b..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/host/AcidimgHost.java +++ /dev/null @@ -1,122 +0,0 @@ -package tn.mnlr.vripper.host; - -import lombok.extern.slf4j.Slf4j; -import org.apache.http.NameValuePair; -import org.apache.http.client.HttpClient; -import org.apache.http.client.entity.UrlEncodedFormEntity; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.protocol.HttpClientContext; -import org.apache.http.message.BasicNameValuePair; -import org.apache.http.util.EntityUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.w3c.dom.Document; -import org.w3c.dom.Node; -import tn.mnlr.vripper.exception.HostException; -import tn.mnlr.vripper.exception.XpathException; -import tn.mnlr.vripper.services.ConnectionService; -import tn.mnlr.vripper.services.HostService; -import tn.mnlr.vripper.services.HtmlProcessorService; -import tn.mnlr.vripper.services.XpathService; - -import java.util.ArrayList; -import java.util.List; - -@Service -@Slf4j -public class AcidimgHost extends Host { - - private static final String host = "acidimg.cc"; - private static final String CONTINUE_BUTTON_XPATH = "//input[@id='continuebutton']"; - private static final String IMG_XPATH = "//img[@class='centred']"; - - private final ConnectionService cm; - private final HostService hostService; - private final XpathService xpathService; - private final HtmlProcessorService htmlProcessorService; - - @Autowired - public AcidimgHost( - ConnectionService cm, - HostService hostService, - XpathService xpathService, - HtmlProcessorService htmlProcessorService) { - this.cm = cm; - this.hostService = hostService; - this.xpathService = xpathService; - this.htmlProcessorService = htmlProcessorService; - } - - @Override - public String getHost() { - return host; - } - - @Override - public String getLookup() { - return host; - } - - @Override - public HostService.NameUrl getNameAndUrl(final String url, final HttpClientContext context) - throws HostException { - - Document doc = hostService.getResponse(url, context).getDocument(); - - Node contDiv; - try { - log.debug(String.format("Looking for xpath expression %s in %s", CONTINUE_BUTTON_XPATH, url)); - contDiv = xpathService.getAsNode(doc, CONTINUE_BUTTON_XPATH); - } catch (XpathException e) { - throw new HostException(e); - } - - if (contDiv != null) { - log.debug(String.format("Click button found for %s", url)); - HttpClient client = cm.getClient().build(); - HttpPost httpPost = cm.buildHttpPost(url, context); - httpPost.addHeader("Referer", url); - List params = new ArrayList<>(); - params.add(new BasicNameValuePair("imgContinue", "Continue to your image")); - try { - httpPost.setEntity(new UrlEncodedFormEntity(params)); - } catch (Exception e) { - throw new HostException(e); - } - - log.debug(String.format("Requesting %s", httpPost)); - try (CloseableHttpResponse response = - (CloseableHttpResponse) client.execute(httpPost, context)) { - log.debug(String.format("Cleaning response for %s", httpPost)); - doc = htmlProcessorService.clean(EntityUtils.toString(response.getEntity())); - EntityUtils.consumeQuietly(response.getEntity()); - } catch (Exception e) { - throw new HostException(e); - } - } - - Node imgNode; - try { - log.debug(String.format("Looking for xpath expression %s in %s", IMG_XPATH, url)); - imgNode = xpathService.getAsNode(doc, IMG_XPATH); - } catch (XpathException e) { - throw new HostException(e); - } - - if (imgNode == null) { - throw new HostException("Cannot find the image node"); - } - - try { - log.debug(String.format("Resolving name and image url for %s", url)); - String imgTitle = imgNode.getAttributes().getNamedItem("alt").getTextContent().trim(); - String imgUrl = imgNode.getAttributes().getNamedItem("src").getTextContent().trim(); - - return new HostService.NameUrl( - imgTitle.isEmpty() ? hostService.getDefaultImageName(imgUrl) : imgTitle, imgUrl); - } catch (Exception e) { - throw new HostException("Unexpected error occurred", e); - } - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/host/DPicMeHost.java b/vripper-server/src/main/java/tn/mnlr/vripper/host/DPicMeHost.java deleted file mode 100644 index 40e03b28..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/host/DPicMeHost.java +++ /dev/null @@ -1,85 +0,0 @@ -package tn.mnlr.vripper.host; - -import lombok.extern.slf4j.Slf4j; -import org.apache.http.client.protocol.HttpClientContext; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.w3c.dom.Document; -import org.w3c.dom.Node; -import tn.mnlr.vripper.exception.HostException; -import tn.mnlr.vripper.exception.XpathException; -import tn.mnlr.vripper.services.HostService; -import tn.mnlr.vripper.services.XpathService; - -import java.util.Optional; -import java.util.UUID; - -@Service -@Slf4j -public class DPicMeHost extends Host { - - private static final String IMG_XPATH = "//img[@id='pic']"; - private static final String host = "dpic.me"; - - private final HostService hostService; - private final XpathService xpathService; - - @Autowired - public DPicMeHost(HostService hostService, XpathService xpathService) { - this.hostService = hostService; - this.xpathService = xpathService; - } - - @Override - public String getHost() { - return host; - } - - @Override - public String getLookup() { - return host; - } - - @Override - public HostService.NameUrl getNameAndUrl(final String url, final HttpClientContext context) - throws HostException { - - HostService.Response response = - hostService.getResponse(url.replace("http://", "https://"), context); - Document doc = response.getDocument(); - - Node imgNode; - try { - log.debug(String.format("Looking for xpath expression %s in %s", IMG_XPATH, url)); - imgNode = xpathService.getAsNode(doc, IMG_XPATH); - } catch (XpathException e) { - throw new HostException(e); - } - - if (imgNode == null) { - throw new HostException(String.format("Xpath '%s' cannot be found in '%s'", IMG_XPATH, url)); - } - - try { - log.debug(String.format("Resolving name and image url for %s", url)); - String imgTitle = - Optional.ofNullable(imgNode.getAttributes().getNamedItem("alt")) - .map(e -> e.getTextContent().trim()) - .orElse(""); - String imgUrl = - Optional.ofNullable(imgNode.getAttributes().getNamedItem("src")) - .map(e -> e.getTextContent().trim()) - .orElse(""); - String defaultName = UUID.randomUUID().toString(); - - int index = imgUrl.lastIndexOf('/'); - if (index != -1 && index < imgUrl.length()) { - defaultName = imgUrl.substring(imgUrl.lastIndexOf('/') + 1); - } - - return new HostService.NameUrl(imgTitle.isEmpty() ? defaultName : imgTitle, imgUrl); - } catch (Exception e) { - throw new HostException("Unexpected error occurred", e); - } - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/host/Host.java b/vripper-server/src/main/java/tn/mnlr/vripper/host/Host.java deleted file mode 100644 index d7d3c38d..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/host/Host.java +++ /dev/null @@ -1,43 +0,0 @@ -package tn.mnlr.vripper.host; - -import lombok.extern.slf4j.Slf4j; -import org.apache.http.client.protocol.HttpClientContext; -import org.springframework.stereotype.Service; -import tn.mnlr.vripper.exception.HostException; -import tn.mnlr.vripper.services.HostService; - -import java.util.Objects; - -@Service -@Slf4j -public abstract class Host { - - public abstract String getHost(); - - public abstract String getLookup(); - - public boolean isSupported(String url) { - return url.contains(getLookup()); - } - - public abstract HostService.NameUrl getNameAndUrl( - final String url, final HttpClientContext context) throws HostException; - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - Host host = (Host) o; - return Objects.equals(getHost(), host.getHost()); - } - - @Override - public int hashCode() { - return Objects.hash(getHost()); - } - - @Override - public String toString() { - return getHost(); - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/host/ImageBamHost.java b/vripper-server/src/main/java/tn/mnlr/vripper/host/ImageBamHost.java deleted file mode 100644 index 04741eb3..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/host/ImageBamHost.java +++ /dev/null @@ -1,96 +0,0 @@ -package tn.mnlr.vripper.host; - -import lombok.extern.slf4j.Slf4j; -import org.apache.http.client.protocol.HttpClientContext; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.w3c.dom.Document; -import org.w3c.dom.Node; -import tn.mnlr.vripper.exception.HostException; -import tn.mnlr.vripper.exception.XpathException; -import tn.mnlr.vripper.services.HostService; -import tn.mnlr.vripper.services.XpathService; - -import java.util.Optional; -import java.util.UUID; - -@Service -@Slf4j -public class ImageBamHost extends Host { - - private static final String host = "imagebam.com"; - private static final String IMG_XPATH = "//img[contains(@class,'main-image')]"; - private static final String CONTINUE_XPATH = "//*[contains(text(), 'Continue')]"; - - private final HostService hostService; - private final XpathService xpathService; - - @Autowired - public ImageBamHost(HostService hostService, XpathService xpathService) { - this.hostService = hostService; - this.xpathService = xpathService; - } - - @Override - public String getHost() { - return host; - } - - @Override - public String getLookup() { - return host; - } - - @Override - public HostService.NameUrl getNameAndUrl(final String url, final HttpClientContext context) - throws HostException { - - HostService.Response response = hostService.getResponse(url, context); - Document doc = response.getDocument(); - - try { - log.debug(String.format("Looking for xpath expression %s in %s", CONTINUE_XPATH, url)); - if (xpathService.getAsNode(doc, CONTINUE_XPATH) != null) { - // Button detected. No need to actually click it, just make the call again. - response = hostService.getResponse(url, context); - doc = response.getDocument(); - } - } catch (XpathException e) { - throw new HostException(e); - } - - Node imgNode; - try { - log.debug(String.format("Looking for xpath expression %s in %s", IMG_XPATH, url)); - imgNode = xpathService.getAsNode(doc, IMG_XPATH); - } catch (XpathException e) { - throw new HostException(e); - } - - if (imgNode == null) { - throw new HostException(String.format("Xpath '%s' cannot be found in '%s'", IMG_XPATH, url)); - } - - try { - log.debug(String.format("Resolving name and image url for %s", url)); - String imgTitle = - Optional.ofNullable(imgNode.getAttributes().getNamedItem("alt")) - .map(e -> e.getTextContent().trim()) - .orElse(""); - String imgUrl = - Optional.ofNullable(imgNode.getAttributes().getNamedItem("src")) - .map(e -> e.getTextContent().trim()) - .orElse(""); - String defaultName = UUID.randomUUID().toString(); - - int index = imgUrl.lastIndexOf('/'); - if (index != -1 && index < imgUrl.length()) { - defaultName = imgUrl.substring(imgUrl.lastIndexOf('/') + 1); - } - - return new HostService.NameUrl(imgTitle.isEmpty() ? defaultName : imgTitle, imgUrl); - } catch (Exception e) { - throw new HostException("Unexpected error occurred", e); - } - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/host/ImageTwistHost.java b/vripper-server/src/main/java/tn/mnlr/vripper/host/ImageTwistHost.java deleted file mode 100644 index ad8fb73f..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/host/ImageTwistHost.java +++ /dev/null @@ -1,70 +0,0 @@ -package tn.mnlr.vripper.host; - -import lombok.extern.slf4j.Slf4j; -import org.apache.http.client.protocol.HttpClientContext; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.w3c.dom.Document; -import org.w3c.dom.Node; -import tn.mnlr.vripper.exception.HostException; -import tn.mnlr.vripper.exception.XpathException; -import tn.mnlr.vripper.services.HostService; -import tn.mnlr.vripper.services.XpathService; - -import java.util.Optional; - -@Service -@Slf4j -public class ImageTwistHost extends Host { - - private static final String IMG_XPATH = "//img[contains(@class, 'img')]"; - private static final String host = "imagetwist.com"; - - private final HostService hostService; - private final XpathService xpathService; - - @Autowired - public ImageTwistHost(HostService hostService, XpathService xpathService) { - this.hostService = hostService; - this.xpathService = xpathService; - } - - @Override - public String getHost() { - return host; - } - - @Override - public String getLookup() { - return host; - } - - @Override - public HostService.NameUrl getNameAndUrl(final String url, final HttpClientContext context) - throws HostException { - - Document doc = hostService.getResponse(url, context).getDocument(); - - Node imgNode; - try { - log.debug(String.format("Looking for xpath expression %s in %s", IMG_XPATH, url)); - imgNode = xpathService.getAsNode(doc, IMG_XPATH); - } catch (XpathException e) { - throw new HostException(e); - } - - try { - log.debug(String.format("Resolving name and image url for %s", url)); - String imgTitle = - Optional.ofNullable(imgNode.getAttributes().getNamedItem("alt")) - .map(Node::getTextContent) - .map(String::trim) - .orElse(null); - String imgUrl = imgNode.getAttributes().getNamedItem("src").getTextContent().trim(); - - return new HostService.NameUrl(imgTitle, imgUrl); - } catch (Exception e) { - throw new HostException("Unexpected error occurred", e); - } - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/host/ImageVenueHost.java b/vripper-server/src/main/java/tn/mnlr/vripper/host/ImageVenueHost.java deleted file mode 100644 index 9b6a8336..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/host/ImageVenueHost.java +++ /dev/null @@ -1,84 +0,0 @@ -package tn.mnlr.vripper.host; - -import lombok.extern.slf4j.Slf4j; -import org.apache.http.client.protocol.HttpClientContext; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.w3c.dom.Document; -import org.w3c.dom.Node; -import tn.mnlr.vripper.exception.HostException; -import tn.mnlr.vripper.exception.XpathException; -import tn.mnlr.vripper.services.HostService; -import tn.mnlr.vripper.services.XpathService; - -@Service -@Slf4j -public class ImageVenueHost extends Host { - - private static final String host = "imagevenue.com"; - private static final String CONTINUE_BUTTON_XPATH = "//a[@title='Continue to ImageVenue']"; - private static final String IMG_XPATH = "//a[@data-toggle='full']/img"; - - private final HostService hostService; - private final XpathService xpathService; - - @Autowired - public ImageVenueHost(HostService hostService, XpathService xpathService) { - this.hostService = hostService; - this.xpathService = xpathService; - } - - @Override - public String getHost() { - return host; - } - - @Override - public String getLookup() { - return host; - } - - @Override - public HostService.NameUrl getNameAndUrl(final String _url, final HttpClientContext context) - throws HostException { - - String url = _url.replace("http://", "https://"); - - HostService.Response resp = hostService.getResponse(url, context); - Document doc = resp.getDocument(); - - try { - log.debug(String.format("Looking for xpath expression %s in %s", CONTINUE_BUTTON_XPATH, url)); - if (xpathService.getAsNode(doc, CONTINUE_BUTTON_XPATH) != null) { - // Button detected. No need to actually click it, just make the call again. - resp = hostService.getResponse(url, context); - doc = resp.getDocument(); - } - } catch (XpathException e) { - throw new HostException(e); - } - - Node imgNode; - try { - log.debug(String.format("Looking for xpath expression %s in %s", IMG_XPATH, url)); - imgNode = xpathService.getAsNode(doc, IMG_XPATH); - } catch (XpathException e) { - throw new HostException(e); - } - - if (imgNode == null) { - throw new HostException("Failed to locate image"); - } - - try { - log.debug(String.format("Resolving name and image url for %s", url)); - String imgTitle = imgNode.getAttributes().getNamedItem("alt").getTextContent().trim(); - String imgUrl = imgNode.getAttributes().getNamedItem("src").getTextContent().trim(); - - return new HostService.NameUrl( - imgTitle.isEmpty() ? imgUrl.substring(imgUrl.lastIndexOf('/') + 1) : imgTitle, imgUrl); - } catch (Exception e) { - throw new HostException("Unexpected error occurred", e); - } - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/host/ImageZillaHost.java b/vripper-server/src/main/java/tn/mnlr/vripper/host/ImageZillaHost.java deleted file mode 100644 index fe166576..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/host/ImageZillaHost.java +++ /dev/null @@ -1,71 +0,0 @@ -package tn.mnlr.vripper.host; - -import lombok.extern.slf4j.Slf4j; -import org.apache.http.client.protocol.HttpClientContext; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.w3c.dom.Document; -import org.w3c.dom.Node; -import tn.mnlr.vripper.exception.HostException; -import tn.mnlr.vripper.exception.XpathException; -import tn.mnlr.vripper.services.HostService; -import tn.mnlr.vripper.services.XpathService; - -@Service -@Slf4j -public class ImageZillaHost extends Host { - - private static final String host = "imagezilla.net"; - private static final String lookup = "imagezilla.net/show"; - private static final String IMG_XPATH = "//img[@id='photo']"; - - private final HostService hostService; - private final XpathService xpathService; - - @Autowired - public ImageZillaHost(HostService hostService, XpathService xpathService) { - this.hostService = hostService; - this.xpathService = xpathService; - } - - @Override - public String getHost() { - return host; - } - - @Override - public String getLookup() { - return lookup; - } - - @Override - public HostService.NameUrl getNameAndUrl(final String url, final HttpClientContext context) - throws HostException { - - Document doc = hostService.getResponse(url, context).getDocument(); - - String title; - try { - log.debug(String.format("Looking for xpath expression %s in %s", IMG_XPATH, url)); - Node titleNode = xpathService.getAsNode(doc, IMG_XPATH).getAttributes().getNamedItem("title"); - log.debug(String.format("Resolving name for %s", url)); - if (titleNode != null) { - title = titleNode.getTextContent().trim(); - } else { - title = null; - } - } catch (XpathException e) { - throw new HostException(e); - } - - if (title == null || title.isEmpty()) { - title = hostService.getDefaultImageName(url); - } - - try { - return new HostService.NameUrl(title, url.replace("show", "images")); - } catch (Exception e) { - throw new HostException("Unexpected error occurred", e); - } - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/host/ImgSpiceHost.java b/vripper-server/src/main/java/tn/mnlr/vripper/host/ImgSpiceHost.java deleted file mode 100644 index 205bb996..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/host/ImgSpiceHost.java +++ /dev/null @@ -1,67 +0,0 @@ -package tn.mnlr.vripper.host; - -import lombok.extern.slf4j.Slf4j; -import org.apache.http.client.protocol.HttpClientContext; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.w3c.dom.Document; -import org.w3c.dom.Node; -import tn.mnlr.vripper.exception.HostException; -import tn.mnlr.vripper.exception.XpathException; -import tn.mnlr.vripper.services.HostService; -import tn.mnlr.vripper.services.XpathService; - -@Service -@Slf4j -public class ImgSpiceHost extends Host { - - private static final String host = "imgspice.com"; - private static final String IMG_XPATH = "//img[@id='imgpreview']"; - - private final HostService hostService; - private final XpathService xpathService; - - @Autowired - public ImgSpiceHost(HostService hostService, XpathService xpathService) { - this.hostService = hostService; - this.xpathService = xpathService; - } - - @Override - public String getHost() { - return host; - } - - @Override - public String getLookup() { - return host; - } - - @Override - public HostService.NameUrl getNameAndUrl(final String _url, final HttpClientContext context) - throws HostException { - - String url = _url.replace("http://", "https://"); - HostService.Response resp = hostService.getResponse(url, context); - Document doc = resp.getDocument(); - - Node imgNode; - try { - log.debug(String.format("Looking for xpath expression %s in %s", IMG_XPATH, url)); - imgNode = xpathService.getAsNode(doc, IMG_XPATH); - } catch (XpathException e) { - throw new HostException(e); - } - - try { - log.debug(String.format("Resolving name and image url for %s", url)); - String imgTitle = imgNode.getAttributes().getNamedItem("alt").getTextContent().trim(); - String imgUrl = imgNode.getAttributes().getNamedItem("src").getTextContent().trim(); - - return new HostService.NameUrl( - imgTitle.isEmpty() ? imgUrl.substring(imgUrl.lastIndexOf('/') + 1) : imgTitle, imgUrl); - } catch (Exception e) { - throw new HostException("Unexpected error occurred", e); - } - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/host/ImgboxHost.java b/vripper-server/src/main/java/tn/mnlr/vripper/host/ImgboxHost.java deleted file mode 100644 index 593facf1..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/host/ImgboxHost.java +++ /dev/null @@ -1,64 +0,0 @@ -package tn.mnlr.vripper.host; - -import lombok.extern.slf4j.Slf4j; -import org.apache.http.client.protocol.HttpClientContext; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.w3c.dom.Document; -import org.w3c.dom.Node; -import tn.mnlr.vripper.exception.HostException; -import tn.mnlr.vripper.exception.XpathException; -import tn.mnlr.vripper.services.HostService; -import tn.mnlr.vripper.services.XpathService; - -@Service -@Slf4j -public class ImgboxHost extends Host { - - private static final String host = "imgbox.com"; - private static final String IMG_XPATH = "//img[@id='img']"; - - private final HostService hostService; - private final XpathService xpathService; - - @Autowired - public ImgboxHost(HostService hostService, XpathService xpathService) { - this.hostService = hostService; - this.xpathService = xpathService; - } - - @Override - public String getHost() { - return host; - } - - @Override - public String getLookup() { - return host; - } - - @Override - public HostService.NameUrl getNameAndUrl(final String url, final HttpClientContext context) - throws HostException { - - Document doc = hostService.getResponse(url, context).getDocument(); - - Node imgNode; - try { - log.debug(String.format("Looking for xpath expression %s in %s", IMG_XPATH, url)); - imgNode = xpathService.getAsNode(doc, IMG_XPATH); - } catch (XpathException e) { - throw new HostException(e); - } - - try { - log.debug(String.format("Resolving name and image url for %s", url)); - String imgTitle = imgNode.getAttributes().getNamedItem("title").getTextContent().trim(); - String imgUrl = imgNode.getAttributes().getNamedItem("src").getTextContent().trim(); - - return new HostService.NameUrl(imgTitle, imgUrl); - } catch (Exception e) { - throw new HostException("Unexpected error occurred", e); - } - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/host/ImxHost.java b/vripper-server/src/main/java/tn/mnlr/vripper/host/ImxHost.java deleted file mode 100644 index b4167023..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/host/ImxHost.java +++ /dev/null @@ -1,129 +0,0 @@ -package tn.mnlr.vripper.host; - -import lombok.extern.slf4j.Slf4j; -import org.apache.http.NameValuePair; -import org.apache.http.client.HttpClient; -import org.apache.http.client.entity.UrlEncodedFormEntity; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.protocol.HttpClientContext; -import org.apache.http.message.BasicNameValuePair; -import org.apache.http.util.EntityUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.w3c.dom.Document; -import org.w3c.dom.Node; -import tn.mnlr.vripper.exception.HostException; -import tn.mnlr.vripper.exception.HtmlProcessorException; -import tn.mnlr.vripper.exception.XpathException; -import tn.mnlr.vripper.services.ConnectionService; -import tn.mnlr.vripper.services.HostService; -import tn.mnlr.vripper.services.HtmlProcessorService; -import tn.mnlr.vripper.services.XpathService; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -@Service -@Slf4j -public class ImxHost extends Host { - - private static final String host = "imx.to"; - private static final String CONTINUE_BUTTON_XPATH = "//*[@name='imgContinue']"; - private static final String IMG_XPATH = "//img[@class='centred']"; - - private final HostService hostService; - private final XpathService xpathService; - private final ConnectionService cm; - private final HtmlProcessorService htmlProcessorService; - - @Autowired - public ImxHost( - HostService hostService, - XpathService xpathService, - ConnectionService cm, - HtmlProcessorService htmlProcessorService) { - this.hostService = hostService; - this.xpathService = xpathService; - this.cm = cm; - this.htmlProcessorService = htmlProcessorService; - } - - @Override - public String getHost() { - return host; - } - - @Override - public String getLookup() { - return host; - } - - @Override - public HostService.NameUrl getNameAndUrl(final String _url, final HttpClientContext context) - throws HostException { - - String url = _url.replace("http://", "https://"); - HostService.Response resp = hostService.getResponse(url, context); - Document doc = resp.getDocument(); - - Node contDiv; - String value = null; - try { - log.debug(String.format("Looking for xpath expression %s in %s", CONTINUE_BUTTON_XPATH, url)); - contDiv = xpathService.getAsNode(doc, CONTINUE_BUTTON_XPATH); - if (contDiv == null) { - throw new HostException(CONTINUE_BUTTON_XPATH + " cannot be found"); - } - Node node = contDiv.getAttributes().getNamedItem("value"); - if (node != null) { - value = node.getTextContent(); - } - } catch (XpathException e) { - throw new HostException(e); - } - - if (value == null) { - throw new HostException("Failed to obtain value attribute from continue input"); - } - log.debug(String.format("Click button found for %s", url)); - HttpClient client = cm.getClient().build(); - HttpPost httpPost = cm.buildHttpPost(url, context); - List params = new ArrayList<>(); - params.add(new BasicNameValuePair("imgContinue", value)); - try { - httpPost.setEntity(new UrlEncodedFormEntity(params)); - } catch (Exception e) { - throw new HostException(e); - } - log.debug(String.format("Requesting %s", httpPost)); - try (CloseableHttpResponse response = - (CloseableHttpResponse) client.execute(httpPost, context)) { - log.debug(String.format("Cleaning response for %s", httpPost)); - doc = htmlProcessorService.clean(EntityUtils.toString(response.getEntity())); - EntityUtils.consumeQuietly(response.getEntity()); - } catch (IOException | HtmlProcessorException e) { - throw new HostException(e); - } - - Node imgNode; - try { - log.debug(String.format("Looking for xpath expression %s in %s", IMG_XPATH, url)); - imgNode = xpathService.getAsNode(doc, IMG_XPATH); - } catch (XpathException e) { - throw new HostException(e); - } - - try { - log.debug(String.format("Resolving name and image url for %s", url)); - String imgTitle = imgNode.getAttributes().getNamedItem("alt").getTextContent().trim(); - String imgUrl = imgNode.getAttributes().getNamedItem("src").getTextContent().trim(); - - return new HostService.NameUrl( - imgTitle.isEmpty() ? imgUrl.substring(imgUrl.lastIndexOf('/') + 1) : imgTitle, imgUrl); - } catch (Exception e) { - throw new HostException("Unexpected error occurred", e); - } - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/host/PimpandhostHost.java b/vripper-server/src/main/java/tn/mnlr/vripper/host/PimpandhostHost.java deleted file mode 100644 index cc79b460..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/host/PimpandhostHost.java +++ /dev/null @@ -1,75 +0,0 @@ -package tn.mnlr.vripper.host; - -import lombok.extern.slf4j.Slf4j; -import org.apache.http.client.protocol.HttpClientContext; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.w3c.dom.Document; -import org.w3c.dom.Node; -import tn.mnlr.vripper.exception.HostException; -import tn.mnlr.vripper.exception.XpathException; -import tn.mnlr.vripper.services.HostService; -import tn.mnlr.vripper.services.XpathService; - -@Service -@Slf4j -public class PimpandhostHost extends Host { - - private static final String host = "pimpandhost.com"; - private static final String IMG_XPATH = "//img[contains(@class, 'original')]"; - - private final HostService hostService; - private final XpathService xpathService; - - @Autowired - public PimpandhostHost(HostService hostService, XpathService xpathService) { - super(); - this.hostService = hostService; - this.xpathService = xpathService; - } - - @Override - public String getHost() { - return host; - } - - @Override - public String getLookup() { - return host; - } - - @Override - public HostService.NameUrl getNameAndUrl(final String _url, final HttpClientContext context) - throws HostException { - - String url; - try { - url = hostService.appendUri(_url.replace("http://", "https://"), "size=original"); - } catch (Exception e) { - throw new HostException(e); - } - - HostService.Response resp = hostService.getResponse(url, context); - Document doc = resp.getDocument(); - - Node imgNode; - try { - log.debug(String.format("Looking for xpath expression %s in %s", IMG_XPATH, url)); - imgNode = xpathService.getAsNode(doc, IMG_XPATH); - } catch (XpathException e) { - throw new HostException(e); - } - - try { - log.debug(String.format("Resolving name and image url for %s", url)); - String imgTitle = imgNode.getAttributes().getNamedItem("alt").getTextContent().trim(); - String imgUrl = - "https:" + imgNode.getAttributes().getNamedItem("src").getTextContent().trim(); - - return new HostService.NameUrl( - imgTitle.isEmpty() ? imgUrl.substring(imgUrl.lastIndexOf('/') + 1) : imgTitle, imgUrl); - } catch (Exception e) { - throw new HostException("Unexpected error occurred", e); - } - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/host/PixRouteHost.java b/vripper-server/src/main/java/tn/mnlr/vripper/host/PixRouteHost.java deleted file mode 100644 index 767a6347..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/host/PixRouteHost.java +++ /dev/null @@ -1,69 +0,0 @@ -package tn.mnlr.vripper.host; - -import lombok.extern.slf4j.Slf4j; -import org.apache.http.client.protocol.HttpClientContext; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.w3c.dom.Document; -import org.w3c.dom.Node; -import tn.mnlr.vripper.exception.HostException; -import tn.mnlr.vripper.exception.XpathException; -import tn.mnlr.vripper.services.HostService; -import tn.mnlr.vripper.services.XpathService; - -@Service -@Slf4j -public class PixRouteHost extends Host { - - private static final String host = "pixroute.com"; - private static final String IMG_XPATH = "//img[@id='imgpreview']"; - - private final HostService hostService; - private final XpathService xpathService; - - @Autowired - public PixRouteHost(HostService hostService, XpathService xpathService) { - this.hostService = hostService; - this.xpathService = xpathService; - } - - @Override - public String getHost() { - return host; - } - - @Override - public String getLookup() { - return host; - } - - @Override - public HostService.NameUrl getNameAndUrl(final String _url, final HttpClientContext context) - throws HostException { - - String url = _url.replace("http://", "https://"); - Document doc = hostService.getResponse(url, context).getDocument(); - - Node imgNode; - try { - log.debug(String.format("Looking for xpath expression %s in %s", IMG_XPATH, url)); - imgNode = xpathService.getAsNode(doc, IMG_XPATH); - } catch (XpathException e) { - throw new HostException(e); - } - - if (imgNode == null) { - throw new HostException("Failed to locate image"); - } - - try { - log.debug(String.format("Resolving name and image url for %s", url)); - - return new HostService.NameUrl( - imgNode.getAttributes().getNamedItem("alt").getTextContent().trim(), - imgNode.getAttributes().getNamedItem("src").getTextContent().trim()); - } catch (Exception e) { - throw new HostException("Unexpected error occurred", e); - } - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/host/PixhostHost.java b/vripper-server/src/main/java/tn/mnlr/vripper/host/PixhostHost.java deleted file mode 100644 index c61cad8c..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/host/PixhostHost.java +++ /dev/null @@ -1,65 +0,0 @@ -package tn.mnlr.vripper.host; - -import lombok.extern.slf4j.Slf4j; -import org.apache.http.client.protocol.HttpClientContext; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.w3c.dom.Document; -import org.w3c.dom.Node; -import tn.mnlr.vripper.exception.HostException; -import tn.mnlr.vripper.exception.XpathException; -import tn.mnlr.vripper.services.HostService; -import tn.mnlr.vripper.services.XpathService; - -@Service -@Slf4j -public class PixhostHost extends Host { - - private static final String host = "pixhost.to"; - private static final String lookup = "pixhost.to/show"; - private static final String IMG_XPATH = "//img[@id='image']"; - - private final HostService hostService; - private final XpathService xpathService; - - @Autowired - public PixhostHost(HostService hostService, XpathService xpathService) { - this.hostService = hostService; - this.xpathService = xpathService; - } - - @Override - public String getHost() { - return host; - } - - @Override - public String getLookup() { - return lookup; - } - - @Override - public HostService.NameUrl getNameAndUrl(final String url, final HttpClientContext context) - throws HostException { - - Document doc = hostService.getResponse(url, context).getDocument(); - - Node imgNode; - try { - log.debug(String.format("Looking for xpath expression %s in %s", IMG_XPATH, url)); - imgNode = xpathService.getAsNode(doc, IMG_XPATH); - } catch (XpathException e) { - throw new HostException(e); - } - - try { - log.debug(String.format("Resolving name and image url for %s", url)); - String imgTitle = imgNode.getAttributes().getNamedItem("alt").getTextContent().trim(); - String imgUrl = imgNode.getAttributes().getNamedItem("src").getTextContent().trim(); - - return new HostService.NameUrl(imgTitle.substring(imgTitle.indexOf('_') + 1), imgUrl); - } catch (Exception e) { - throw new HostException("Unexpected error occurred", e); - } - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/host/PixxxelsHost.java b/vripper-server/src/main/java/tn/mnlr/vripper/host/PixxxelsHost.java deleted file mode 100644 index b0d899ae..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/host/PixxxelsHost.java +++ /dev/null @@ -1,72 +0,0 @@ -package tn.mnlr.vripper.host; - -import lombok.extern.slf4j.Slf4j; -import org.apache.http.client.protocol.HttpClientContext; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.w3c.dom.Document; -import org.w3c.dom.Node; -import tn.mnlr.vripper.exception.HostException; -import tn.mnlr.vripper.exception.XpathException; -import tn.mnlr.vripper.services.HostService; -import tn.mnlr.vripper.services.XpathService; - -@Service -@Slf4j -public class PixxxelsHost extends Host { - - private static final String host = "pixxxels.cc"; - private static final String IMG_XPATH = "//*[@id='download']"; - private static final String TITLE_XPATH = "//*[contains(@class,'imagename')]"; - - private final HostService hostService; - private final XpathService xpathService; - - @Autowired - public PixxxelsHost(HostService hostService, XpathService xpathService) { - this.hostService = hostService; - this.xpathService = xpathService; - } - - @Override - public String getHost() { - return host; - } - - @Override - public String getLookup() { - return host; - } - - @Override - public HostService.NameUrl getNameAndUrl(final String _url, final HttpClientContext context) - throws HostException { - - String url = _url.replace("http://", "https://"); - HostService.Response resp = hostService.getResponse(url, context); - Document doc = resp.getDocument(); - - Node imgNode, titleNode; - try { - log.debug(String.format("Looking for xpath expression %s in %s", IMG_XPATH, url)); - imgNode = xpathService.getAsNode(doc, IMG_XPATH); - - log.debug(String.format("Looking for xpath expression %s in %s", TITLE_XPATH, url)); - titleNode = xpathService.getAsNode(doc, TITLE_XPATH); - - } catch (XpathException e) { - throw new HostException(e); - } - - try { - log.debug(String.format("Resolving name and image url for %s", url)); - String imgTitle = titleNode.getTextContent().trim(); - String imgUrl = imgNode.getAttributes().getNamedItem("href").getTextContent().trim(); - - return new HostService.NameUrl( - imgTitle.isEmpty() ? imgUrl.substring(imgUrl.lastIndexOf('/') + 1) : imgTitle, imgUrl); - } catch (Exception e) { - throw new HostException("Unexpected error occurred", e); - } - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/host/PostImgHost.java b/vripper-server/src/main/java/tn/mnlr/vripper/host/PostImgHost.java deleted file mode 100644 index 6456bbf3..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/host/PostImgHost.java +++ /dev/null @@ -1,74 +0,0 @@ -package tn.mnlr.vripper.host; - -import lombok.extern.slf4j.Slf4j; -import org.apache.http.client.protocol.HttpClientContext; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.w3c.dom.Document; -import org.w3c.dom.Node; -import tn.mnlr.vripper.exception.HostException; -import tn.mnlr.vripper.exception.XpathException; -import tn.mnlr.vripper.services.HostService; -import tn.mnlr.vripper.services.XpathService; - -import java.util.Optional; - -@Service -@Slf4j -public class PostImgHost extends Host { - - private static final String host = "postimg.cc"; - private static final String TITLE_XPATH = "//span[contains(@class,'imagename')]"; - private static final String IMG_XPATH = "//a[@id='download']"; - - private final HostService hostService; - private final XpathService xpathService; - - @Autowired - public PostImgHost(HostService hostService, XpathService xpathService) { - this.hostService = hostService; - this.xpathService = xpathService; - } - - @Override - public String getHost() { - return host; - } - - @Override - public String getLookup() { - return host; - } - - @Override - public HostService.NameUrl getNameAndUrl(final String _url, final HttpClientContext context) - throws HostException { - - String url = _url.replace("http://", "https://"); - Document doc = hostService.getResponse(url, context).getDocument(); - - Node urlNode, titleNode; - try { - log.debug(String.format("Looking for xpath expression %s in %s", TITLE_XPATH, url)); - titleNode = xpathService.getAsNode(doc, TITLE_XPATH); - - log.debug(String.format("Looking for xpath expression %s in %s", IMG_XPATH, url)); - urlNode = xpathService.getAsNode(doc, IMG_XPATH); - } catch (XpathException e) { - throw new HostException(e); - } - - try { - log.debug(String.format("Resolving name and image url for %s", url)); - String imgTitle = - Optional.ofNullable(titleNode) - .map(node -> node.getTextContent().trim()) - .orElseGet(() -> hostService.getDefaultImageName(url)); - - return new HostService.NameUrl( - imgTitle, urlNode.getAttributes().getNamedItem("href").getTextContent().trim()); - } catch (Exception e) { - throw new HostException("Unexpected error occurred", e); - } - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/host/TurboImageHost.java b/vripper-server/src/main/java/tn/mnlr/vripper/host/TurboImageHost.java deleted file mode 100644 index 027a7cbd..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/host/TurboImageHost.java +++ /dev/null @@ -1,73 +0,0 @@ -package tn.mnlr.vripper.host; - -import lombok.extern.slf4j.Slf4j; -import org.apache.http.client.protocol.HttpClientContext; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.w3c.dom.Document; -import org.w3c.dom.Node; -import tn.mnlr.vripper.exception.HostException; -import tn.mnlr.vripper.exception.XpathException; -import tn.mnlr.vripper.services.HostService; -import tn.mnlr.vripper.services.XpathService; - -@Service -@Slf4j -public class TurboImageHost extends Host { - - private static final String host = "turboimagehost.com"; - private static final String TITLE_XPATH = "//div[contains(@class,'titleFullS')]/h1"; - private static final String IMG_XPATH = "//img[@id='imageid']"; - - private final HostService hostService; - private final XpathService xpathService; - - @Autowired - public TurboImageHost(HostService hostService, XpathService xpathService) { - this.hostService = hostService; - this.xpathService = xpathService; - } - - @Override - public String getHost() { - return host; - } - - @Override - public String getLookup() { - return host; - } - - @Override - public HostService.NameUrl getNameAndUrl(final String url, final HttpClientContext context) - throws HostException { - - Document doc = hostService.getResponse(url, context).getDocument(); - - String title; - try { - log.debug(String.format("Looking for xpath expression %s in %s", TITLE_XPATH, url)); - Node titleNode = xpathService.getAsNode(doc, TITLE_XPATH); - log.debug(String.format("Resolving name for %s", url)); - if (titleNode != null) { - title = titleNode.getTextContent().trim(); - } else { - title = null; - } - } catch (XpathException e) { - throw new HostException(e); - } - - if (title == null || title.isEmpty()) { - title = hostService.getDefaultImageName(url); - } - - try { - Node urlNode = xpathService.getAsNode(doc, IMG_XPATH); - return new HostService.NameUrl( - title, urlNode.getAttributes().getNamedItem("src").getTextContent().trim()); - } catch (Exception e) { - throw new HostException("Unexpected error occurred", e); - } - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/host/ViprImHost.java b/vripper-server/src/main/java/tn/mnlr/vripper/host/ViprImHost.java deleted file mode 100644 index 6aa9e2d0..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/host/ViprImHost.java +++ /dev/null @@ -1,70 +0,0 @@ -package tn.mnlr.vripper.host; - -import lombok.extern.slf4j.Slf4j; -import org.apache.http.client.protocol.HttpClientContext; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.w3c.dom.Document; -import org.w3c.dom.Node; -import tn.mnlr.vripper.exception.HostException; -import tn.mnlr.vripper.exception.XpathException; -import tn.mnlr.vripper.services.HostService; -import tn.mnlr.vripper.services.XpathService; - -import java.util.Optional; - -@Service -@Slf4j -public class ViprImHost extends Host { - - private static final String IMG_XPATH = "//img[contains(@class, 'img')]"; - private static final String host = "vipr.im"; - - private final HostService hostService; - private final XpathService xpathService; - - @Autowired - public ViprImHost(HostService hostService, XpathService xpathService) { - this.hostService = hostService; - this.xpathService = xpathService; - } - - @Override - public String getHost() { - return host; - } - - @Override - public String getLookup() { - return host; - } - - @Override - public HostService.NameUrl getNameAndUrl(final String url, final HttpClientContext context) - throws HostException { - - Document doc = hostService.getResponse(url, context).getDocument(); - - Node imgNode; - try { - log.debug(String.format("Looking for xpath expression %s in %s", IMG_XPATH, url)); - imgNode = xpathService.getAsNode(doc, IMG_XPATH); - } catch (XpathException e) { - throw new HostException(e); - } - - try { - log.debug(String.format("Resolving name and image url for %s", url)); - String imgTitle = - Optional.ofNullable(imgNode.getAttributes().getNamedItem("alt")) - .map(Node::getTextContent) - .map(String::trim) - .orElse(null); - String imgUrl = imgNode.getAttributes().getNamedItem("src").getTextContent().trim(); - - return new HostService.NameUrl(imgTitle, imgUrl); - } catch (Exception e) { - throw new HostException("Unexpected error occurred", e); - } - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/jpa/Management.java b/vripper-server/src/main/java/tn/mnlr/vripper/jpa/Management.java deleted file mode 100644 index dbe10a4c..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/jpa/Management.java +++ /dev/null @@ -1,79 +0,0 @@ -package tn.mnlr.vripper.jpa; - -import org.springframework.beans.factory.annotation.Value; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.scheduling.annotation.EnableScheduling; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Component; -import tn.mnlr.vripper.download.DownloadService; -import tn.mnlr.vripper.services.ThreadPoolService; - -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; -import java.io.File; -import java.time.LocalDate; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.util.Optional; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -@Component -@EnableScheduling -public class Management { - - public static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd"); - private static final Pattern BACKUP_FILE_PATTERN = - Pattern.compile("^db_(\\d{4}-\\d{2}-\\d{2})\\.tar\\.gz$"); - - private final JdbcTemplate jdbcTemplate; - private final String backupFolder; - private final ThreadPoolService threadPoolService; - private final DownloadService downloadService; - - public Management( - JdbcTemplate jdbcTemplate, - @Value("${base.dir}") String baseDir, - @Value("${base.dir.name}") String baseDirName, - ThreadPoolService threadPoolService, - DownloadService downloadService) { - this.jdbcTemplate = jdbcTemplate; - this.threadPoolService = threadPoolService; - this.downloadService = downloadService; - this.backupFolder = baseDir + File.separator + baseDirName + File.separator + "backup"; - } - - @PreDestroy - public void destroy() { - - try { - threadPoolService.destroy(); - downloadService.destroy(); - this.jdbcTemplate.execute("SHUTDOWN"); - } catch (Exception ignored) { - Thread.currentThread().interrupt(); - } - } - - @PostConstruct - @Scheduled(cron = "0 0 0 ? * *") - private void backup() { - - for (File file : - Optional.ofNullable(new File(backupFolder).listFiles()).orElse(new File[] {})) { - Matcher matcher = BACKUP_FILE_PATTERN.matcher(file.getName()); - if (matcher.find()) { - LocalDate localDate = LocalDate.parse(matcher.group(1), FORMATTER); - if (localDate.isEqual(LocalDate.now())) { - return; - } - } - } - - // create a backup file - File backupFile = - new File( - backupFolder, String.format("db_%s.tar.gz", LocalDateTime.now().format(FORMATTER))); - jdbcTemplate.execute(String.format("BACKUP DATABASE TO '%s' BLOCKING", backupFile.getPath())); - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/jpa/domain/DateTimeSerializer.java b/vripper-server/src/main/java/tn/mnlr/vripper/jpa/domain/DateTimeSerializer.java deleted file mode 100644 index a33ba1fe..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/jpa/domain/DateTimeSerializer.java +++ /dev/null @@ -1,25 +0,0 @@ -package tn.mnlr.vripper.jpa.domain; - -import com.fasterxml.jackson.core.JsonGenerator; -import com.fasterxml.jackson.databind.SerializerProvider; -import com.fasterxml.jackson.databind.ser.std.StdSerializer; - -import java.io.IOException; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; - -public class DateTimeSerializer extends StdSerializer { - - private static final DateTimeFormatter DATE_TIME_FORMATTER = - DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss.SSS"); - - protected DateTimeSerializer() { - super(LocalDateTime.class); - } - - @Override - public void serialize(LocalDateTime value, JsonGenerator gen, SerializerProvider provider) - throws IOException { - gen.writeString(value.format(DATE_TIME_FORMATTER)); - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/jpa/domain/Image.java b/vripper-server/src/main/java/tn/mnlr/vripper/jpa/domain/Image.java deleted file mode 100644 index 07a0b6a4..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/jpa/domain/Image.java +++ /dev/null @@ -1,66 +0,0 @@ -package tn.mnlr.vripper.jpa.domain; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.ToString; -import tn.mnlr.vripper.host.Host; -import tn.mnlr.vripper.jpa.domain.enums.Status; - -import java.util.Objects; - -@Getter -@Setter -@ToString -@NoArgsConstructor -public class Image { - - @JsonIgnore protected Long id; - - @JsonIgnore private Host host; - - private String url; - - private int index; - - private long current = 0; - - private long total = 0; - - private Status status; - - private String postId; - - @JsonIgnore private Long postIdRef; - - public Image(String postId, String url, Host host, int index) { - this.postId = postId; - this.url = url; - this.host = host; - this.index = index; - status = Status.STOPPED; - } - - public void increase(int read) { - current += read; - } - - public void init() { - current = 0; - status = Status.STOPPED; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - Image image = (Image) o; - return Objects.equals(url, image.url); - } - - @Override - public int hashCode() { - return Objects.hash(url); - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/jpa/domain/LogEvent.java b/vripper-server/src/main/java/tn/mnlr/vripper/jpa/domain/LogEvent.java deleted file mode 100644 index 74ea3392..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/jpa/domain/LogEvent.java +++ /dev/null @@ -1,52 +0,0 @@ -package tn.mnlr.vripper.jpa.domain; - -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.ToString; - -import java.time.LocalDateTime; - -@Getter -@Setter -@ToString -@NoArgsConstructor -public class LogEvent { - - private Long id; - private Type type; - private Status status; - - @JsonSerialize(using = DateTimeSerializer.class) - private LocalDateTime time; - - private String message; - - public LogEvent(Type type, Status status, LocalDateTime time, String message) { - this.type = type; - this.status = status; - this.time = time; - this.message = message; - } - - public enum Type { - POST, - QUEUED, - THANKS, - METADATA, - SCAN, - DOWNLOAD, - - // Below are deprecated events - METADATA_CACHE_MISS, - QUEUED_CACHE_MISS - } - - public enum Status { - PENDING, - PROCESSING, - DONE, - ERROR - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/jpa/domain/Metadata.java b/vripper-server/src/main/java/tn/mnlr/vripper/jpa/domain/Metadata.java deleted file mode 100644 index adaf61d2..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/jpa/domain/Metadata.java +++ /dev/null @@ -1,31 +0,0 @@ -package tn.mnlr.vripper.jpa.domain; - -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; - -import java.util.Collections; -import java.util.List; - -@Getter -@Setter -@NoArgsConstructor -public class Metadata { - - private Long postIdRef; - - private String postId; - - private String postedBy; - - private List resolvedNames = Collections.emptyList(); - - public static Metadata from(Metadata metadata) { - Metadata copy = new Metadata(); - copy.postIdRef = metadata.postIdRef; - copy.postId = metadata.postId; - copy.postedBy = metadata.postedBy; - copy.resolvedNames = List.copyOf(metadata.resolvedNames); - return copy; - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/jpa/domain/Post.java b/vripper-server/src/main/java/tn/mnlr/vripper/jpa/domain/Post.java deleted file mode 100644 index 76f110da..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/jpa/domain/Post.java +++ /dev/null @@ -1,90 +0,0 @@ -package tn.mnlr.vripper.jpa.domain; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.ToString; -import tn.mnlr.vripper.jpa.domain.enums.Status; - -import java.time.LocalDateTime; -import java.util.Collections; -import java.util.Objects; -import java.util.Set; - -@Getter -@Setter -@ToString -@NoArgsConstructor -public class Post { - - @JsonIgnore private Long id; - - private Status status; - - private String postId; - - private String threadTitle; - - private String threadId; - - private String title; - - private String url; - - private int done; - - private int total; - - private Set hosts; - - private String forum; - - @JsonIgnore private String securityToken; - - @JsonIgnore private String downloadDirectory; - - private boolean thanked; - - private Set previews = Collections.emptySet(); - - private Metadata metadata; - - private int rank = Integer.MAX_VALUE; - - @JsonSerialize(using = DateTimeSerializer.class) - private LocalDateTime addedOn; - - public Post( - String title, - String url, - String postId, - String threadId, - String threadTitle, - String forum, - String securityToken) { - this.title = title; - this.url = url; - this.postId = postId; - this.forum = forum; - this.threadId = threadId; - this.threadTitle = threadTitle; - this.securityToken = securityToken; - status = Status.STOPPED; - addedOn = LocalDateTime.now(); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - Post post = (Post) o; - return Objects.equals(postId, post.postId); - } - - @Override - public int hashCode() { - return Objects.hash(postId); - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/jpa/domain/Queued.java b/vripper-server/src/main/java/tn/mnlr/vripper/jpa/domain/Queued.java deleted file mode 100644 index d71019f6..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/jpa/domain/Queued.java +++ /dev/null @@ -1,56 +0,0 @@ -package tn.mnlr.vripper.jpa.domain; - -import com.fasterxml.jackson.annotation.JsonIgnore; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.ToString; - -import java.util.Objects; - -@Getter -@Setter -@NoArgsConstructor -@ToString -public class Queued { - - @JsonIgnore private Long id; - - private String link; - - private String threadId; - - private String postId; - - private int total = 0; - - private boolean loading = true; - - public Queued(String link, String threadId, String postId) { - this(); - this.link = link; - this.threadId = threadId; - this.postId = postId; - } - - public void done() { - this.loading = false; - } - - public void increment() { - this.total++; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - Queued that = (Queued) o; - return Objects.equals(threadId, that.threadId); - } - - @Override - public int hashCode() { - return Objects.hash(threadId); - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/jpa/domain/enums/Status.java b/vripper-server/src/main/java/tn/mnlr/vripper/jpa/domain/enums/Status.java deleted file mode 100644 index 08a98b4c..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/jpa/domain/enums/Status.java +++ /dev/null @@ -1,10 +0,0 @@ -package tn.mnlr.vripper.jpa.domain.enums; - -public enum Status { - PENDING, - DOWNLOADING, - COMPLETE, - ERROR, - STOPPED, - PARTIAL -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/jpa/repositories/IImageRepository.java b/vripper-server/src/main/java/tn/mnlr/vripper/jpa/repositories/IImageRepository.java deleted file mode 100644 index 831842f5..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/jpa/repositories/IImageRepository.java +++ /dev/null @@ -1,32 +0,0 @@ -package tn.mnlr.vripper.jpa.repositories; - -import tn.mnlr.vripper.jpa.domain.Image; -import tn.mnlr.vripper.jpa.domain.enums.Status; - -import java.util.List; -import java.util.Optional; - -public interface IImageRepository extends IRepository { - - Image save(Image image); - - void deleteAllByPostId(String postId); - - List findByPostId(String postId); - - Integer countError(); - - List findByPostIdAndIsNotCompleted(String postId); - - int stopByPostIdAndIsNotCompleted(String postId); - - List findByPostIdAndIsError(String postId); - - Optional findById(Long id); - - int updateStatus(Status status, Long id); - - int updateCurrent(long current, Long id); - - int updateTotal(long total, Long id); -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/jpa/repositories/ILogEventRepository.java b/vripper-server/src/main/java/tn/mnlr/vripper/jpa/repositories/ILogEventRepository.java deleted file mode 100644 index 4a175db6..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/jpa/repositories/ILogEventRepository.java +++ /dev/null @@ -1,21 +0,0 @@ -package tn.mnlr.vripper.jpa.repositories; - -import tn.mnlr.vripper.jpa.domain.LogEvent; - -import java.util.List; -import java.util.Optional; - -public interface ILogEventRepository extends IRepository { - - LogEvent save(LogEvent logEvent); - - LogEvent update(LogEvent logEvent); - - List findAll(); - - Optional findById(Long id); - - void delete(Long id); - - void deleteAll(); -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/jpa/repositories/IMetadataRepository.java b/vripper-server/src/main/java/tn/mnlr/vripper/jpa/repositories/IMetadataRepository.java deleted file mode 100644 index 81752e5d..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/jpa/repositories/IMetadataRepository.java +++ /dev/null @@ -1,14 +0,0 @@ -package tn.mnlr.vripper.jpa.repositories; - -import tn.mnlr.vripper.jpa.domain.Metadata; - -import java.util.Optional; - -public interface IMetadataRepository extends IRepository { - - Metadata save(Metadata metadata); - - Optional findByPostId(String postId); - - int deleteByPostId(String postId); -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/jpa/repositories/IPostRepository.java b/vripper-server/src/main/java/tn/mnlr/vripper/jpa/repositories/IPostRepository.java deleted file mode 100644 index 7365774c..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/jpa/repositories/IPostRepository.java +++ /dev/null @@ -1,38 +0,0 @@ -package tn.mnlr.vripper.jpa.repositories; - -import tn.mnlr.vripper.jpa.domain.Post; -import tn.mnlr.vripper.jpa.domain.enums.Status; - -import java.util.List; -import java.util.Optional; - -public interface IPostRepository extends IRepository { - - Post save(Post post); - - Optional findByPostId(String postId); - - Optional findById(Long id); - - List findCompleted(); - - List findAll(); - - boolean existByPostId(String postId); - - int setDownloadingToStopped(); - - int deleteByPostId(String postId); - - int updateStatus(Status status, Long id); - - int updateDone(int done, Long id); - - int updateFolderName(String postFolderName, Long id); - - int updateTitle(String title, Long id); - - int updateThanked(boolean thanked, Long id); - - int updateRank(int rank, Long id); -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/jpa/repositories/IQueuedRepository.java b/vripper-server/src/main/java/tn/mnlr/vripper/jpa/repositories/IQueuedRepository.java deleted file mode 100644 index aab60339..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/jpa/repositories/IQueuedRepository.java +++ /dev/null @@ -1,20 +0,0 @@ -package tn.mnlr.vripper.jpa.repositories; - -import tn.mnlr.vripper.jpa.domain.Queued; - -import java.util.List; -import java.util.Optional; - -public interface IQueuedRepository extends IRepository { - Queued save(Queued queued); - - Optional findByThreadId(String threadId); - - List findAll(); - - Optional findById(Long id); - - int deleteByThreadId(String threadId); - - void deleteAll(); -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/jpa/repositories/IRepository.java b/vripper-server/src/main/java/tn/mnlr/vripper/jpa/repositories/IRepository.java deleted file mode 100644 index 854550e3..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/jpa/repositories/IRepository.java +++ /dev/null @@ -1,3 +0,0 @@ -package tn.mnlr.vripper.jpa.repositories; - -public interface IRepository {} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/jpa/repositories/impl/ImageRepository.java b/vripper-server/src/main/java/tn/mnlr/vripper/jpa/repositories/impl/ImageRepository.java deleted file mode 100644 index d88f9b04..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/jpa/repositories/impl/ImageRepository.java +++ /dev/null @@ -1,156 +0,0 @@ -package tn.mnlr.vripper.jpa.repositories.impl; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.core.RowMapper; -import org.springframework.stereotype.Service; -import tn.mnlr.vripper.SpringContext; -import tn.mnlr.vripper.event.Event; -import tn.mnlr.vripper.event.EventBus; -import tn.mnlr.vripper.host.Host; -import tn.mnlr.vripper.jpa.domain.Image; -import tn.mnlr.vripper.jpa.domain.enums.Status; -import tn.mnlr.vripper.jpa.repositories.IImageRepository; - -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.List; -import java.util.Optional; - -@Service -public class ImageRepository implements IImageRepository { - - private final JdbcTemplate jdbcTemplate; - private final EventBus eventBus; - - @Autowired - public ImageRepository(JdbcTemplate jdbcTemplate, EventBus eventBus) { - this.jdbcTemplate = jdbcTemplate; - this.eventBus = eventBus; - } - - private synchronized Long nextId() { - return jdbcTemplate.queryForObject("CALL NEXT VALUE FOR SEQ_IMAGE", Long.class); - } - - @Override - public Image save(Image image) { - long id = nextId(); - jdbcTemplate.update( - "INSERT INTO IMAGE (ID, CURRENT, HOST, INDEX, POST_ID, STATUS, TOTAL, URL, POST_ID_REF) VALUES (?,?,?,?,?,?,?,?,?)", - id, - image.getCurrent(), - image.getHost().getHost(), - image.getIndex(), - image.getPostId(), - image.getStatus().name(), - image.getTotal(), - image.getUrl(), - image.getPostIdRef()); - image.setId(id); - eventBus.publishEvent(Event.wrap(Event.Kind.IMAGE_UPDATE, id)); - return image; - } - - @Override - public void deleteAllByPostId(String postId) { - jdbcTemplate.update("DELETE FROM IMAGE WHERE POST_ID = ?", postId); - } - - @Override - public List findByPostId(String postId) { - return jdbcTemplate.query( - "SELECT * FROM IMAGE WHERE POST_ID = ?", new ImageRowMapper(), postId); - } - - @Override - public Integer countError() { - return jdbcTemplate.queryForObject( - "SELECT COUNT(*) FROM IMAGE AS image WHERE image.STATUS = 'ERROR'", Integer.class); - } - - @Override - public List findByPostIdAndIsNotCompleted(String postId) { - return jdbcTemplate.query( - "SELECT * FROM IMAGE AS image WHERE image.POST_ID = ? AND image.STATUS <> 'COMPLETE'", - new ImageRowMapper(), - postId); - } - - @Override - public int stopByPostIdAndIsNotCompleted(String postId) { - return jdbcTemplate.update( - "UPDATE IMAGE AS image SET image.STATUS = 'STOPPED' WHERE image.POST_ID = ? AND image.STATUS <> 'COMPLETE'", - postId); - } - - @Override - public List findByPostIdAndIsError(String postId) { - return jdbcTemplate.query( - "SELECT * FROM IMAGE AS image WHERE image.POST_ID = ? AND image.STATUS = 'ERROR'", - new ImageRowMapper(), - postId); - } - - @Override - public Optional findById(Long id) { - List images = - jdbcTemplate.query( - "SELECT * FROM IMAGE AS image WHERE image.ID = ?", new ImageRowMapper(), id); - if (images.isEmpty()) { - return Optional.empty(); - } else { - return Optional.of(images.get(0)); - } - } - - @Override - public int updateStatus(Status status, Long id) { - int mutationCount = - jdbcTemplate.update( - "UPDATE IMAGE AS image SET image.STATUS = ? WHERE image.ID = ?", status.name(), id); - eventBus.publishEvent(Event.wrap(Event.Kind.IMAGE_UPDATE, id)); - return mutationCount; - } - - @Override - public int updateCurrent(long current, Long id) { - int mutationCount = - jdbcTemplate.update( - "UPDATE IMAGE AS image SET image.CURRENT = ? WHERE image.ID = ?", current, id); - eventBus.publishEvent(Event.wrap(Event.Kind.IMAGE_UPDATE, id)); - return mutationCount; - } - - @Override - public int updateTotal(long total, Long id) { - int mutationCount = - jdbcTemplate.update( - "UPDATE IMAGE AS image SET image.TOTAL = ? WHERE image.ID = ?", total, id); - eventBus.publishEvent(Event.wrap(Event.Kind.IMAGE_UPDATE, id)); - return mutationCount; - } -} - -class ImageRowMapper implements RowMapper { - - @Override - public Image mapRow(ResultSet rs, int rowNum) throws SQLException { - Image image = new Image(); - image.setId(rs.getLong("ID")); - String host = rs.getString("HOST"); - image.setHost( - SpringContext.getBeansOfType(Host.class).values().stream() - .filter(e -> e.getHost().equals(host)) - .findAny() - .orElse(null)); - image.setUrl(rs.getString("URL")); - image.setIndex(rs.getInt("INDEX")); - image.setCurrent(rs.getLong("CURRENT")); - image.setTotal(rs.getLong("TOTAL")); - image.setStatus(Status.valueOf(rs.getString("STATUS"))); - image.setPostId(rs.getString("POST_ID")); - image.setPostIdRef(rs.getLong("POST_ID_REF")); - return image; - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/jpa/repositories/impl/LogEventRepository.java b/vripper-server/src/main/java/tn/mnlr/vripper/jpa/repositories/impl/LogEventRepository.java deleted file mode 100644 index 7e90452f..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/jpa/repositories/impl/LogEventRepository.java +++ /dev/null @@ -1,125 +0,0 @@ -package tn.mnlr.vripper.jpa.repositories.impl; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.core.RowMapper; -import org.springframework.lang.NonNull; -import org.springframework.stereotype.Service; -import tn.mnlr.vripper.event.Event; -import tn.mnlr.vripper.event.EventBus; -import tn.mnlr.vripper.jpa.domain.LogEvent; -import tn.mnlr.vripper.jpa.repositories.ILogEventRepository; -import tn.mnlr.vripper.services.SettingsService; - -import java.sql.ResultSet; -import java.sql.SQLException; -import java.time.LocalDateTime; -import java.time.format.DateTimeFormatter; -import java.util.List; -import java.util.Optional; - -@Service -@Slf4j -public class LogEventRepository implements ILogEventRepository { - - private final JdbcTemplate jdbcTemplate; - private final SettingsService settingsService; - private final EventBus eventBus; - - public LogEventRepository( - JdbcTemplate jdbcTemplate, SettingsService settingsService, EventBus eventBus) { - this.jdbcTemplate = jdbcTemplate; - this.settingsService = settingsService; - this.eventBus = eventBus; - } - - private synchronized Long nextId() { - return jdbcTemplate.queryForObject("CALL NEXT VALUE FOR SEQ_EVENT", Long.class); - } - - @Override - public synchronized LogEvent save(@NonNull LogEvent logEvent) { - - int maxRecords = settingsService.getSettings().getMaxEventLog() - 1; - - Long count = jdbcTemplate.queryForObject("SELECT COUNT(*) FROM EVENT", Long.class); - if (count > maxRecords) { - List idList = - jdbcTemplate.queryForList( - "SELECT ID FROM EVENT ORDER BY TIME ASC LIMIT ?", Long.class, count - maxRecords); - idList.forEach(this::delete); - } - - long id = nextId(); - jdbcTemplate.update( - "INSERT INTO EVENT (ID, TYPE, STATUS, TIME, MESSAGE) VALUES (?,?,?,?,?)", - id, - logEvent.getType().name(), - logEvent.getStatus().name(), - logEvent.getTime().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME), - logEvent.getMessage()); - logEvent.setId(id); - eventBus.publishEvent(Event.wrap(Event.Kind.LOG_EVENT_UPDATE, id)); - return logEvent; - } - - @Override - public LogEvent update(@NonNull LogEvent logEvent) { - if (logEvent.getId() == null) { - log.warn("Cannot update entity with null id"); - return logEvent; - } - - jdbcTemplate.update( - "UPDATE EVENT SET TYPE = ?, STATUS = ?, TIME = ?, MESSAGE = ? WHERE ID = ?", - logEvent.getType().name(), - logEvent.getStatus().name(), - logEvent.getTime().format(DateTimeFormatter.ISO_LOCAL_DATE_TIME), - logEvent.getMessage(), - logEvent.getId()); - eventBus.publishEvent(Event.wrap(Event.Kind.LOG_EVENT_UPDATE, logEvent.getId())); - return logEvent; - } - - @Override - public Optional findById(Long id) { - List logEvents = - jdbcTemplate.query("SELECT * FROM EVENT WHERE ID = ?", new LogEventRowMapper(), id); - if (logEvents.isEmpty()) { - return Optional.empty(); - } else { - return Optional.of(logEvents.get(0)); - } - } - - @Override - public List findAll() { - return jdbcTemplate.query("SELECT * FROM EVENT", new LogEventRowMapper()); - } - - @Override - public void delete(Long id) { - jdbcTemplate.update("DELETE FROM EVENT WHERE ID = ?", id); - eventBus.publishEvent(Event.wrap(Event.Kind.LOG_EVENT_REMOVE, id)); - } - - @Override - public void deleteAll() { - jdbcTemplate.update("DELETE FROM EVENT"); - } -} - -class LogEventRowMapper implements RowMapper { - - @Override - public LogEvent mapRow(ResultSet rs, int rowNum) throws SQLException { - LogEvent logEvent = new LogEvent(); - logEvent.setId(rs.getLong("ID")); - logEvent.setType(LogEvent.Type.valueOf(rs.getString("TYPE"))); - logEvent.setStatus(LogEvent.Status.valueOf(rs.getString("STATUS"))); - logEvent.setTime( - LocalDateTime.parse(rs.getString("TIME"), DateTimeFormatter.ISO_LOCAL_DATE_TIME)); - logEvent.setMessage(rs.getString("MESSAGE")); - return logEvent; - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/jpa/repositories/impl/MetadataRepository.java b/vripper-server/src/main/java/tn/mnlr/vripper/jpa/repositories/impl/MetadataRepository.java deleted file mode 100644 index 82d6a145..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/jpa/repositories/impl/MetadataRepository.java +++ /dev/null @@ -1,76 +0,0 @@ -package tn.mnlr.vripper.jpa.repositories.impl; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.core.RowMapper; -import org.springframework.stereotype.Service; -import tn.mnlr.vripper.event.Event; -import tn.mnlr.vripper.event.EventBus; -import tn.mnlr.vripper.jpa.domain.Metadata; -import tn.mnlr.vripper.jpa.repositories.IMetadataRepository; - -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.List; -import java.util.Optional; - -@Service -public class MetadataRepository implements IMetadataRepository { - - private final JdbcTemplate jdbcTemplate; - private final EventBus eventBus; - - @Autowired - public MetadataRepository(JdbcTemplate jdbcTemplate, EventBus eventBus) { - this.jdbcTemplate = jdbcTemplate; - this.eventBus = eventBus; - } - - @Override - public Metadata save(Metadata metadata) { - jdbcTemplate.update( - "INSERT INTO METADATA (POST_ID_REF, POST_ID, POSTED_BY, RESOLVED_NAMES) VALUES (?,?,?,?)", - metadata.getPostIdRef(), - metadata.getPostId(), - metadata.getPostedBy(), - String.join("%sep%", metadata.getResolvedNames())); - eventBus.publishEvent(Event.wrap(Event.Kind.METADATA_UPDATE, metadata.getPostIdRef())); - return metadata; - } - - @Override - public Optional findByPostId(String postId) { - List metadata = - jdbcTemplate.query( - "SELECT metadata.* FROM METADATA AS metadata WHERE metadata.POST_ID = ?", - new MetadataRowMapper(), - postId); - if (metadata.isEmpty()) { - return Optional.empty(); - } else { - return Optional.of(metadata.get(0)); - } - } - - @Override - public int deleteByPostId(String postId) { - return jdbcTemplate.update( - "DELETE FROM METADATA AS metadata WHERE metadata.POST_ID = ?", postId); - } -} - -class MetadataRowMapper implements RowMapper { - - @Override - public Metadata mapRow(ResultSet rs, int rowNum) throws SQLException { - Metadata metadata = new Metadata(); - metadata.setPostIdRef(rs.getLong("POST_ID_REF")); - metadata.setPostId(rs.getString("POST_ID")); - metadata.setPostedBy(rs.getString("POSTED_BY")); - String resolvedNames = rs.getString("RESOLVED_NAMES"); - if (resolvedNames != null && !resolvedNames.isBlank()) { - metadata.setResolvedNames(List.of(resolvedNames.split("%sep%"))); - } - return metadata; - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/jpa/repositories/impl/PostRepository.java b/vripper-server/src/main/java/tn/mnlr/vripper/jpa/repositories/impl/PostRepository.java deleted file mode 100644 index d77293b9..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/jpa/repositories/impl/PostRepository.java +++ /dev/null @@ -1,230 +0,0 @@ -package tn.mnlr.vripper.jpa.repositories.impl; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.core.RowMapper; -import org.springframework.stereotype.Service; -import tn.mnlr.vripper.event.Event; -import tn.mnlr.vripper.event.EventBus; -import tn.mnlr.vripper.jpa.domain.Metadata; -import tn.mnlr.vripper.jpa.domain.Post; -import tn.mnlr.vripper.jpa.domain.enums.Status; -import tn.mnlr.vripper.jpa.repositories.IPostRepository; - -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Timestamp; -import java.util.List; -import java.util.Optional; -import java.util.Set; - -@Service -public class PostRepository implements IPostRepository { - - private final JdbcTemplate jdbcTemplate; - private final EventBus eventBus; - - @Autowired - public PostRepository(JdbcTemplate jdbcTemplate, EventBus eventBus) { - this.jdbcTemplate = jdbcTemplate; - this.eventBus = eventBus; - } - - private synchronized Long nextId() { - return jdbcTemplate.queryForObject("CALL NEXT VALUE FOR SEQ_POST", Long.class); - } - - @Override - public Post save(Post post) { - long id = nextId(); - jdbcTemplate.update( - "INSERT INTO POST (ID, DONE, FORUM, HOSTS, POST_FOLDER_NAME, POST_ID, PREVIEWS, SECURITY_TOKEN, STATUS, THANKED, THREAD_ID, THREAD_TITLE, TITLE, TOTAL, URL, ADDED_ON, RANK) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", - id, - post.getDone(), - post.getForum(), - String.join(";", post.getHosts()), - post.getDownloadDirectory(), - post.getPostId(), - String.join(";", post.getPreviews()), - post.getSecurityToken(), - post.getStatus().name(), - post.isThanked(), - post.getThreadId(), - post.getThreadTitle(), - post.getTitle(), - post.getTotal(), - post.getUrl(), - Timestamp.valueOf(post.getAddedOn()), - post.getRank()); - post.setId(id); - eventBus.publishEvent(Event.wrap(Event.Kind.POST_UPDATE, id)); - return post; - } - - @Override - public Optional findByPostId(String postId) { - List posts = - jdbcTemplate.query( - "SELECT metadata.*,post.* FROM METADATA metadata FULL JOIN POST post ON metadata.POST_ID_REF = post.ID WHERE post.POST_ID = ?", - new PostRowMapper(), - postId); - if (posts.isEmpty()) { - return Optional.empty(); - } else { - return Optional.of(posts.get(0)); - } - } - - @Override - public List findCompleted() { - return jdbcTemplate.query( - "SELECT POST_ID FROM POST AS post WHERE status = 'COMPLETE' AND done >= total", - ((rs, rowNum) -> rs.getString("POST_ID"))); - } - - @Override - public Optional findById(Long id) { - List posts = - jdbcTemplate.query( - "SELECT metadata.*,post.* FROM METADATA metadata FULL JOIN POST post ON metadata.POST_ID_REF = post.ID WHERE post.ID = ?", - new PostRowMapper(), - id); - if (posts.isEmpty()) { - return Optional.empty(); - } else { - return Optional.of(posts.get(0)); - } - } - - @Override - public List findAll() { - return jdbcTemplate.query( - "SELECT metadata.*,post.* FROM METADATA metadata FULL JOIN POST post ON metadata.POST_ID_REF = post.ID", - new PostRowMapper()); - } - - @Override - public boolean existByPostId(String postId) { - Integer count = - jdbcTemplate.queryForObject( - "SELECT COUNT(*) FROM POST AS post WHERE post.POST_ID = ?", Integer.class, postId); - if (count == null) { - return false; - } else { - return count > 0; - } - } - - @Override - public int setDownloadingToStopped() { - return jdbcTemplate.update( - "UPDATE POST AS post SET post.STATUS = 'STOPPED' WHERE post.STATUS = 'DOWNLOADING' OR post.STATUS = 'PARTIAL' OR post.STATUS = 'PENDING'"); - } - - @Override - public int deleteByPostId(String postId) { - int mutationCount = - jdbcTemplate.update("DELETE FROM POST AS post WHERE post.POST_ID = ?", postId); - eventBus.publishEvent(Event.wrap(Event.Kind.POST_REMOVE, postId)); - return mutationCount; - } - - @Override - public int updateStatus(Status status, Long id) { - int mutationCount = - jdbcTemplate.update( - "UPDATE POST AS post SET post.STATUS = ? WHERE post.ID = ?", status.name(), id); - eventBus.publishEvent(Event.wrap(Event.Kind.POST_UPDATE, id)); - return mutationCount; - } - - @Override - public int updateDone(int done, Long id) { - int mutationCount = - jdbcTemplate.update("UPDATE POST AS post SET post.DONE = ? WHERE post.ID = ?", done, id); - eventBus.publishEvent(Event.wrap(Event.Kind.POST_UPDATE, id)); - return mutationCount; - } - - @Override - public int updateFolderName(String postFolderName, Long id) { - int mutationCount = - jdbcTemplate.update( - "UPDATE POST AS post SET post.POST_FOLDER_NAME = ? WHERE post.ID = ?", - postFolderName, - id); - eventBus.publishEvent(Event.wrap(Event.Kind.POST_UPDATE, id)); - return mutationCount; - } - - @Override - public int updateTitle(String title, Long id) { - int mutationCount = - jdbcTemplate.update("UPDATE POST AS post SET post.TITLE = ? WHERE post.ID = ?", title, id); - eventBus.publishEvent(Event.wrap(Event.Kind.POST_UPDATE, id)); - return mutationCount; - } - - @Override - public int updateThanked(boolean thanked, Long id) { - int mutationCount = - jdbcTemplate.update( - "UPDATE POST AS post SET post.THANKED = ? WHERE post.ID = ?", thanked, id); - eventBus.publishEvent(Event.wrap(Event.Kind.POST_UPDATE, id)); - return mutationCount; - } - - @Override - public int updateRank(int rank, Long id) { - int mutationCount = - jdbcTemplate.update("UPDATE POST AS post SET post.RANK = ? WHERE post.ID = ?", rank, id); - eventBus.publishEvent(Event.wrap(Event.Kind.POST_UPDATE, id)); - return mutationCount; - } -} - -class PostRowMapper implements RowMapper { - - private static final String DELIMITER = ";"; - - @Override - public Post mapRow(ResultSet rs, int rowNum) throws SQLException { - - Post post = new Post(); - post.setId(rs.getLong("post.ID")); - post.setStatus(Status.valueOf(rs.getString("post.STATUS"))); - post.setPostId(rs.getString("post.POST_ID")); - post.setThreadTitle(rs.getString("post.THREAD_TITLE")); - post.setThreadId(rs.getString("post.THREAD_ID")); - post.setTitle(rs.getString("post.TITLE")); - post.setUrl(rs.getString("post.URL")); - post.setDone(rs.getInt("post.DONE")); - post.setTotal(rs.getInt("post.TOTAL")); - post.setHosts(Set.of(rs.getString("post.HOSTS").split(DELIMITER))); - post.setForum(rs.getString("post.FORUM")); - post.setSecurityToken(rs.getString("post.SECURITY_TOKEN")); - post.setDownloadDirectory(rs.getString("post.POST_FOLDER_NAME")); - post.setThanked(rs.getBoolean("post.THANKED")); - String previews; - if ((previews = rs.getString("post.PREVIEWS")) != null) { - post.setPreviews(Set.of(previews.split(DELIMITER))); - } - post.setAddedOn(rs.getTimestamp("post.ADDED_ON").toLocalDateTime()); - post.setRank(rs.getInt("post.RANK")); - - Long metadataId = rs.getLong("metadata.POST_ID_REF"); - if (!rs.wasNull()) { - Metadata metadata = new Metadata(); - metadata.setPostIdRef(metadataId); - metadata.setPostId(rs.getString("metadata.POST_ID")); - String resolvedNames = rs.getString("metadata.RESOLVED_NAMES"); - if (resolvedNames != null && !resolvedNames.isBlank()) { - metadata.setResolvedNames(List.of(resolvedNames.split("%sep%"))); - } - metadata.setPostedBy(rs.getString("metadata.POSTED_BY")); - post.setMetadata(metadata); - } - - return post; - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/jpa/repositories/impl/QueuedRepository.java b/vripper-server/src/main/java/tn/mnlr/vripper/jpa/repositories/impl/QueuedRepository.java deleted file mode 100644 index f314bcd9..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/jpa/repositories/impl/QueuedRepository.java +++ /dev/null @@ -1,107 +0,0 @@ -package tn.mnlr.vripper.jpa.repositories.impl; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.jdbc.core.JdbcTemplate; -import org.springframework.jdbc.core.RowMapper; -import org.springframework.stereotype.Service; -import tn.mnlr.vripper.event.Event; -import tn.mnlr.vripper.event.EventBus; -import tn.mnlr.vripper.jpa.domain.Queued; -import tn.mnlr.vripper.jpa.repositories.IQueuedRepository; - -import java.sql.ResultSet; -import java.sql.SQLException; -import java.util.List; -import java.util.Optional; - -@Service -public class QueuedRepository implements IQueuedRepository { - - private final JdbcTemplate jdbcTemplate; - private final EventBus eventBus; - - @Autowired - public QueuedRepository(JdbcTemplate jdbcTemplate, EventBus eventBus) { - this.jdbcTemplate = jdbcTemplate; - this.eventBus = eventBus; - } - - private synchronized Long nextId() { - return jdbcTemplate.queryForObject("CALL NEXT VALUE FOR SEQ_QUEUED", Long.class); - } - - @Override - public Queued save(Queued queued) { - long id = nextId(); - jdbcTemplate.update( - "INSERT INTO QUEUED (ID, TOTAL, LINK, LOADING, POST_ID, THREAD_ID) values (?,?,?,?,?,?)", - id, - queued.getTotal(), - queued.getLink(), - queued.isLoading(), - queued.getPostId(), - queued.getThreadId()); - queued.setId(id); - eventBus.publishEvent(Event.wrap(Event.Kind.QUEUED_UPDATE, id)); - return queued; - } - - @Override - public Optional findByThreadId(String threadId) { - List queuedList = - jdbcTemplate.query( - "SELECT * FROM QUEUED AS queued WHERE queued.THREAD_ID = ?", - new QueuedRowMapper(), - threadId); - if (queuedList.isEmpty()) { - return Optional.empty(); - } else { - return Optional.of(queuedList.get(0)); - } - } - - @Override - public List findAll() { - return jdbcTemplate.query("SELECT * FROM QUEUED", new QueuedRowMapper()); - } - - @Override - public Optional findById(Long id) { - List queuedList = - jdbcTemplate.query( - "SELECT * FROM QUEUED AS queued WHERE queued.ID = ?", new QueuedRowMapper(), id); - if (queuedList.isEmpty()) { - return Optional.empty(); - } else { - return Optional.of(queuedList.get(0)); - } - } - - @Override - public int deleteByThreadId(String threadId) { - int mutationCount = - jdbcTemplate.update("DELETE FROM QUEUED AS queued WHERE THREAD_ID = ?", threadId); - eventBus.publishEvent(Event.wrap(Event.Kind.QUEUED_REMOVE, threadId)); - return mutationCount; - } - - @Override - public void deleteAll() { - jdbcTemplate.update("DELETE FROM QUEUED"); - } -} - -class QueuedRowMapper implements RowMapper { - - @Override - public Queued mapRow(ResultSet rs, int rowNum) throws SQLException { - Queued queued = new Queued(); - queued.setId(rs.getLong("ID")); - queued.setLink(rs.getString("LINK")); - queued.setThreadId(rs.getString("THREAD_ID")); - queued.setPostId(rs.getString("POST_ID")); - queued.setTotal(rs.getInt("TOTAL")); - queued.setLoading(rs.getBoolean("LOADING")); - return queued; - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/services/ConnectionService.java b/vripper-server/src/main/java/tn/mnlr/vripper/services/ConnectionService.java deleted file mode 100644 index c4f9952d..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/services/ConnectionService.java +++ /dev/null @@ -1,154 +0,0 @@ -package tn.mnlr.vripper.services; - -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; -import net.jodah.failsafe.RetryPolicy; -import org.apache.http.client.config.CookieSpecs; -import org.apache.http.client.config.RequestConfig; -import org.apache.http.client.methods.AbstractExecutionAwareRequest; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.protocol.HttpClientContext; -import org.apache.http.impl.client.HttpClientBuilder; -import org.apache.http.impl.client.HttpClients; -import org.apache.http.impl.client.LaxRedirectStrategy; -import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; -import org.springframework.scheduling.annotation.EnableScheduling; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Service; -import reactor.core.Disposable; -import tn.mnlr.vripper.download.DownloadJob; -import tn.mnlr.vripper.event.Event; -import tn.mnlr.vripper.event.EventBus; -import tn.mnlr.vripper.services.domain.Settings; - -import javax.annotation.PreDestroy; -import java.net.URI; -import java.time.temporal.ChronoUnit; -import java.util.List; -import java.util.concurrent.TimeUnit; - -@Service -@EnableScheduling -@Slf4j -public class ConnectionService { - - private final Disposable disposable; - private PoolingHttpClientConnectionManager pcm; - private RequestConfig rc; - - @Getter private RetryPolicy retryPolicy; - - private int connectionTimeout; - private int maxAttempts; - - public ConnectionService(EventBus eventBus, SettingsService settingsService) { - connectionTimeout = settingsService.getSettings().getConnectionTimeout(); - maxAttempts = settingsService.getSettings().getMaxAttempts(); - disposable = - eventBus - .flux() - .filter(e -> e.getKind().equals(Event.Kind.SETTINGS_UPDATE)) - .map(e -> ((Settings) e.getData())) - .subscribe( - settings -> { - if (connectionTimeout != settings.getConnectionTimeout()) { - connectionTimeout = settings.getConnectionTimeout(); - buildRequestConfig(); - } - if (maxAttempts != settings.getMaxAttempts()) { - maxAttempts = settings.getMaxAttempts(); - buildRetryPolicy(); - } - }); - - buildRequestConfig(); - buildRetryPolicy(); - buildConnectionPool(); - } - - @PreDestroy - private void destroy() { - disposable.dispose(); - } - - private void buildRetryPolicy() { - retryPolicy = - new RetryPolicy<>() - .withDelay(2, 5, ChronoUnit.SECONDS) - .withMaxAttempts(maxAttempts) - .onFailedAttempt( - e -> - log.warn( - String.format("#%d tries failed", e.getAttemptCount()), - e.getLastFailure())); - } - - private void buildConnectionPool() { - pcm = new PoolingHttpClientConnectionManager(); - pcm.setMaxTotal(Integer.MAX_VALUE); - pcm.setDefaultMaxPerRoute(Integer.MAX_VALUE); - } - - private void buildRequestConfig() { - rc = - RequestConfig.custom() - .setConnectionRequestTimeout(connectionTimeout * 1000) - .setConnectTimeout(connectionTimeout * 1000) - .setSocketTimeout(connectionTimeout * 1000) - .setCookieSpec(CookieSpecs.STANDARD) - .build(); - } - - @Scheduled(fixedDelay = 5_000) - private void idleConnectionMonitoring() { - pcm.closeExpiredConnections(); - pcm.closeIdleConnections(30, TimeUnit.SECONDS); - } - - public HttpClientBuilder getClient() { - return HttpClients.custom() - .setConnectionManager(pcm) - .setRedirectStrategy(new LaxRedirectStrategy()) - .disableAutomaticRetries() - .setDefaultRequestConfig(rc); - } - - public HttpGet buildHttpGet(String url, final HttpClientContext context) { - HttpGet httpGet = new HttpGet(url.replace(" ", "+")); - httpGet.addHeader( - "User-Agent", - "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36"); - addToContext(context, httpGet); - return httpGet; - } - - public HttpPost buildHttpPost(String url, final HttpClientContext context) { - HttpPost httpPost = new HttpPost(url.replace(" ", "+")); - httpPost.addHeader( - "User-Agent", - "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36"); - addToContext(context, httpPost); - return httpPost; - } - - public HttpGet buildHttpGet(URI uri, final HttpClientContext context) { - HttpGet httpGet = new HttpGet(uri); - httpGet.addHeader( - "User-Agent", - "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36"); - addToContext(context, httpGet); - return httpGet; - } - - public void addToContext(HttpClientContext context, AbstractExecutionAwareRequest request) { - if (context != null) { - List requests = - (List) - context.getAttribute(DownloadJob.ContextAttributes.OPEN_CONNECTION.toString()); - if (requests != null) { - requests.add(request); - } - } - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/services/DataService.java b/vripper-server/src/main/java/tn/mnlr/vripper/services/DataService.java deleted file mode 100644 index df398066..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/services/DataService.java +++ /dev/null @@ -1,244 +0,0 @@ -package tn.mnlr.vripper.services; - -import lombok.NonNull; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; -import tn.mnlr.vripper.jpa.domain.*; -import tn.mnlr.vripper.jpa.domain.enums.Status; -import tn.mnlr.vripper.jpa.repositories.IImageRepository; -import tn.mnlr.vripper.jpa.repositories.IMetadataRepository; -import tn.mnlr.vripper.jpa.repositories.IPostRepository; -import tn.mnlr.vripper.jpa.repositories.IQueuedRepository; -import tn.mnlr.vripper.jpa.repositories.impl.LogEventRepository; - -import javax.annotation.PostConstruct; -import java.util.*; -import java.util.stream.Collectors; - -@Service -@Transactional -@Slf4j -public class DataService { - - private final IPostRepository postRepository; - private final IImageRepository imageRepository; - private final IQueuedRepository queuedRepository; - private final IMetadataRepository metadataRepository; - private final SettingsService settingsService; - private final LogEventRepository eventRepository; - - @Autowired - public DataService( - IPostRepository postRepository, - IImageRepository imageRepository, - IQueuedRepository queuedRepository, - IMetadataRepository metadataRepository, - SettingsService settingsService, - LogEventRepository eventRepository) { - this.postRepository = postRepository; - this.imageRepository = imageRepository; - this.queuedRepository = queuedRepository; - this.metadataRepository = metadataRepository; - this.settingsService = settingsService; - this.eventRepository = eventRepository; - } - - private void save(Post post) { - postRepository.save(post); - } - - private void save(Queued queued) { - queuedRepository.save(queued); - } - - private void save(Image image) { - imageRepository.save(image); - } - - public boolean exists(String postId) { - return postRepository.existByPostId(postId); - } - - public void newPost(Post post, Collection images) { - save(post); - images.forEach( - image -> { - image.setPostIdRef(post.getId()); - save(image); - }); - sortPostsByRank(); - } - - public synchronized void afterJobFinish(Image image, Post post) { - if (image.getStatus().equals(Status.COMPLETE)) { - post.setDone(post.getDone() + 1); - updatePostDone(post.getDone(), post.getId()); - } else if (image.getStatus().equals(Status.ERROR)) { - post.setStatus(Status.PARTIAL); - updatePostStatus(post.getStatus(), post.getId()); - } - } - - private void updatePostDone(int done, Long id) { - postRepository.updateDone(done, id); - } - - public void updatePostStatus(Status status, Long id) { - postRepository.updateStatus(status, id); - } - - public void updatePostThanks(boolean thanked, Long id) { - postRepository.updateThanked(thanked, id); - } - - public void updatePostTitle(String title, Long id) { - postRepository.updateTitle(title, id); - } - - public void updatePostDownloadDirectory(String postFolderName, Long id) { - postRepository.updateFolderName(postFolderName, id); - } - - public void updatePostRank(int rank, Long id) { - postRepository.updateRank(rank, id); - } - - public void finishPost(@NonNull Post post) { - if (!imageRepository.findByPostIdAndIsError(post.getPostId()).isEmpty()) { - post.setStatus(Status.ERROR); - updatePostStatus(post.getStatus(), post.getId()); - } else { - if (post.getDone() < post.getTotal()) { - post.setStatus(Status.STOPPED); - updatePostStatus(post.getStatus(), post.getId()); - } else { - post.setStatus(Status.COMPLETE); - updatePostStatus(post.getStatus(), post.getId()); - if (settingsService.getSettings().getClearCompleted()) { - remove(List.of(post.getPostId())); - } - } - } - } - - private void remove(@NonNull final List postIds) { - for (String postId : postIds) { - imageRepository.deleteAllByPostId(postId); - metadataRepository.deleteByPostId(postId); - postRepository.deleteByPostId(postId); - } - sortPostsByRank(); - } - - public void newQueueLink(@NonNull final Queued queued) { - save(queued); - } - - public void removeQueueLink(@NonNull final String threadId) { - queuedRepository.deleteByThreadId(threadId); - } - - public List clearCompleted() { - List completed = postRepository.findCompleted(); - remove(completed); - return completed; - } - - public void removeAll(final List postIds) { - - remove( - Objects.requireNonNullElse( - postIds, - postRepository.findAll().stream().map(Post::getPostId).collect(Collectors.toList()))); - } - - public List findByPostIdAndIsNotCompleted(@NonNull String postId) { - return imageRepository.findByPostIdAndIsNotCompleted(postId); - } - - public long countErrorImages() { - return imageRepository.countError(); - } - - public List findImagesByPostId(String postId) { - return imageRepository.findByPostId(postId); - } - - public List findAllPosts() { - return postRepository.findAll(); - } - - public Optional findPostByPostId(String postId) { - return postRepository.findByPostId(postId); - } - - public void stopImagesByPostIdAndIsNotCompleted(String postId) { - imageRepository.stopByPostIdAndIsNotCompleted(postId); - } - - public Optional findQueuedByThreadId(String threadId) { - return queuedRepository.findByThreadId(threadId); - } - - public List findAllQueued() { - return queuedRepository.findAll(); - } - - public Optional findById(Long aLong) { - return postRepository.findById(aLong); - } - - public Optional findImageById(Long aLong) { - return imageRepository.findById(aLong); - } - - public Optional findQueuedById(Long aLong) { - return queuedRepository.findById(aLong); - } - - public synchronized void setMetadata(Post post, Metadata metadata) { - if (metadataRepository.findByPostId(post.getPostId()).isEmpty()) { - metadata.setPostIdRef(post.getId()); - metadataRepository.save(metadata); - } - } - - public Optional findMetadataByPostId(String postId) { - return metadataRepository.findByPostId(postId); - } - - public void updateImageStatus(Status status, Long id) { - imageRepository.updateStatus(status, id); - } - - public void updateImageCurrent(long current, Long id) { - imageRepository.updateCurrent(current, id); - } - - public void updateImageTotal(long total, Long id) { - imageRepository.updateTotal(total, id); - } - - public Optional findEventById(Long id) { - return eventRepository.findById(id); - } - - public List findAllEvents() { - return eventRepository.findAll(); - } - - public void clearQueueLinks() { - queuedRepository.deleteAll(); - } - - public synchronized void sortPostsByRank() { - List posts = findAllPosts(); - posts.sort(Comparator.comparing(Post::getAddedOn)); - for (int i = 0; i < posts.size(); i++) { - posts.get(i).setRank(i); - updatePostRank(i, posts.get(i).getId()); - } - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/services/DownloadSpeedService.java b/vripper-server/src/main/java/tn/mnlr/vripper/services/DownloadSpeedService.java deleted file mode 100644 index be24d15f..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/services/DownloadSpeedService.java +++ /dev/null @@ -1,41 +0,0 @@ -package tn.mnlr.vripper.services; - -import lombok.Getter; -import org.springframework.scheduling.annotation.EnableScheduling; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Service; -import tn.mnlr.vripper.event.Event; -import tn.mnlr.vripper.event.EventBus; - -import java.util.concurrent.atomic.AtomicLong; - -@Service -@EnableScheduling -public class DownloadSpeedService { - - private final AtomicLong read = new AtomicLong(0); - private final EventBus eventBus; - @Getter private long currentValue; - private boolean allowWrite = false; - - public DownloadSpeedService(EventBus eventBus) { - this.eventBus = eventBus; - } - - public void increase(long read) { - if (allowWrite) { - this.read.addAndGet(read); - } - } - - @Scheduled(fixedDelay = 1000) - private void calc() { - allowWrite = false; - long newValue = read.getAndSet(0); - if (newValue != currentValue) { - currentValue = newValue; - eventBus.publishEvent(Event.wrap(Event.Kind.BYTES_PER_SECOND, currentValue)); - } - allowWrite = true; - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/services/GlobalStateService.java b/vripper-server/src/main/java/tn/mnlr/vripper/services/GlobalStateService.java deleted file mode 100644 index 73aef1e0..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/services/GlobalStateService.java +++ /dev/null @@ -1,42 +0,0 @@ -package tn.mnlr.vripper.services; - -import lombok.Getter; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.scheduling.annotation.EnableScheduling; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Service; -import tn.mnlr.vripper.download.DownloadService; -import tn.mnlr.vripper.event.Event; -import tn.mnlr.vripper.event.EventBus; -import tn.mnlr.vripper.services.domain.GlobalState; - -@Service -@EnableScheduling -public class GlobalStateService { - - private final DownloadService downloadService; - private final DataService dataService; - private final EventBus eventBus; - @Getter private GlobalState currentState; - - @Autowired - public GlobalStateService( - DownloadService downloadService, DataService dataService, EventBus eventBus) { - this.downloadService = downloadService; - this.dataService = dataService; - this.eventBus = eventBus; - } - - @Scheduled(fixedDelay = 3000) - private void interval() { - GlobalState newGlobalState = - new GlobalState( - downloadService.runningCount(), - downloadService.pendingCount(), - dataService.countErrorImages()); - if (!newGlobalState.equals(currentState)) { - currentState = newGlobalState; - eventBus.publishEvent(Event.wrap(Event.Kind.GLOBAL_STATE, currentState)); - } - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/services/HostService.java b/vripper-server/src/main/java/tn/mnlr/vripper/services/HostService.java deleted file mode 100644 index 1a997df6..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/services/HostService.java +++ /dev/null @@ -1,113 +0,0 @@ -package tn.mnlr.vripper.services; - -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; -import org.apache.http.Header; -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.protocol.HttpClientContext; -import org.apache.http.util.EntityUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.w3c.dom.Document; -import tn.mnlr.vripper.exception.HostException; -import tn.mnlr.vripper.exception.HtmlProcessorException; - -import java.io.IOException; -import java.net.URI; -import java.net.URISyntaxException; - -@Service -@Slf4j -public class HostService { - - private final ConnectionService cm; - private final HtmlProcessorService htmlProcessorService; - - @Autowired - public HostService(ConnectionService cm, HtmlProcessorService htmlProcessorService) { - this.cm = cm; - this.htmlProcessorService = htmlProcessorService; - } - - public Response getResponse(final String url, final HttpClientContext context) - throws HostException { - String basePage; - - HttpClient client = cm.getClient().build(); - HttpGet httpGet = cm.buildHttpGet(url, context); - Header[] headers; - log.debug(String.format("Requesting %s", url)); - try (CloseableHttpResponse response = - (CloseableHttpResponse) client.execute(httpGet, context)) { - if (response.getStatusLine().getStatusCode() / 100 != 2) { - throw new HostException( - String.format( - "Unexpected response code: %d", response.getStatusLine().getStatusCode())); - } - headers = response.getAllHeaders(); - basePage = EntityUtils.toString(response.getEntity()); - log.debug(String.format("%s response: %n%s", url, basePage)); - EntityUtils.consumeQuietly(response.getEntity()); - } catch (IOException e) { - throw new HostException(e); - } - - try { - log.debug(String.format("Cleaning %s response", url)); - return new Response(htmlProcessorService.clean(basePage), headers); - } catch (HtmlProcessorException e) { - throw new HostException(e); - } - } - - public String appendUri(String uri, String appendQuery) throws URISyntaxException { - URI oldUri = new URI(uri); - - String newQuery = oldUri.getQuery(); - if (newQuery == null) { - newQuery = appendQuery; - } else { - newQuery += "&" + appendQuery; - } - - return new URI( - oldUri.getScheme(), - oldUri.getAuthority(), - oldUri.getPath(), - newQuery, - oldUri.getFragment()) - .toString(); - } - - public String getDefaultImageName(final String imgUrl) { - String imageTitle = imgUrl.substring(imgUrl.lastIndexOf('/') + 1); - log.debug(String.format("Extracting name from url %s: %s", imgUrl, imageTitle)); - return imgUrl; - } - - @Getter - public static class Response { - - private final Document document; - private final Header[] headers; - - protected Response(Document document, Header[] headers) { - this.document = document; - this.headers = headers; - } - } - - @Getter - public static class NameUrl { - - private final String name; - private final String url; - - public NameUrl(String name, String url) { - this.name = name; - this.url = url; - } - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/services/HtmlProcessorService.java b/vripper-server/src/main/java/tn/mnlr/vripper/services/HtmlProcessorService.java deleted file mode 100644 index 21576e81..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/services/HtmlProcessorService.java +++ /dev/null @@ -1,22 +0,0 @@ -package tn.mnlr.vripper.services; - -import org.htmlcleaner.CleanerProperties; -import org.htmlcleaner.DomSerializer; -import org.htmlcleaner.HtmlCleaner; -import org.htmlcleaner.TagNode; -import org.springframework.stereotype.Service; -import org.w3c.dom.Document; -import tn.mnlr.vripper.exception.HtmlProcessorException; - -@Service -public class HtmlProcessorService { - - public Document clean(String htmlContent) throws HtmlProcessorException { - try { - TagNode clean = new HtmlCleaner().clean(htmlContent); - return new DomSerializer(new CleanerProperties()).createDOM(clean); - } catch (Exception e) { - throw new HtmlProcessorException(e); - } - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/services/MetadataService.java b/vripper-server/src/main/java/tn/mnlr/vripper/services/MetadataService.java deleted file mode 100644 index fd52ec7d..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/services/MetadataService.java +++ /dev/null @@ -1,50 +0,0 @@ -package tn.mnlr.vripper.services; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; -import tn.mnlr.vripper.jpa.domain.Post; -import tn.mnlr.vripper.tasks.MetadataRunnable; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; - -@Slf4j -@Service -public class MetadataService { - - private final Map fetchingMetadata = new ConcurrentHashMap<>(); - private final ThreadPoolService threadPoolService; - - public MetadataService(ThreadPoolService threadPoolService) { - this.threadPoolService = threadPoolService; - } - - public void startFetchingMetadata(Post post) { - MetadataRunnable runnable = new MetadataRunnable(post); - threadPoolService.getGeneralExecutor().submit(runnable); - fetchingMetadata.put(post.getPostId(), runnable); - } - - public void stopFetchingMetadata(List postIds) { - List stopping = new ArrayList<>(); - - for (Map.Entry entry : this.fetchingMetadata.entrySet()) { - if (postIds.contains(entry.getKey())) { - stopping.add(entry.getValue()); - entry.getValue().stop(); - } - } - - while (!stopping.isEmpty()) { - stopping.removeIf(MetadataRunnable::isFinished); - try { - Thread.sleep(100); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - postIds.forEach(fetchingMetadata::remove); - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/services/PathService.java b/vripper-server/src/main/java/tn/mnlr/vripper/services/PathService.java deleted file mode 100644 index 8184028c..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/services/PathService.java +++ /dev/null @@ -1,126 +0,0 @@ -package tn.mnlr.vripper.services; - -import lombok.Getter; -import lombok.NonNull; -import lombok.extern.slf4j.Slf4j; -import org.springframework.stereotype.Service; -import tn.mnlr.vripper.exception.RenameException; -import tn.mnlr.vripper.jpa.domain.Post; -import tn.mnlr.vripper.services.domain.Settings; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.StandardCopyOption; -import java.util.concurrent.locks.ReentrantLock; - -@Service -@Slf4j -public class PathService { - - private final DataService dataService; - - @Getter private final ReentrantLock directoryAccess = new ReentrantLock(); - - public PathService(DataService dataService) { - this.dataService = dataService; - } - - public final File calcDownloadDirectory(Post post, Settings settings) { - return new File(settings.getDownloadPath(), post.getDownloadDirectory()); - } - - private File getRootFolder( - @NonNull String forum, @NonNull String threadTitle, Settings settings) { - File sourceFolder = - settings.getSubLocation() - ? new File(settings.getDownloadPath(), sanitize(forum)) - : new File(settings.getDownloadPath()); - return settings.getThreadSubLocation() ? new File(sourceFolder, threadTitle) : sourceFolder; - } - - public final void createDefaultPostFolder(Post post, Settings settings) { - File downloadDirectory = getRootFolder(post.getForum(), post.getThreadTitle(), settings); - downloadDirectory = - new File( - downloadDirectory, - settings.getAppendPostId() - ? sanitize(post.getTitle()) + "_" + post.getPostId() - : sanitize(post.getTitle())); - downloadDirectory = makeDir(downloadDirectory); - post.setDownloadDirectory( - downloadDirectory.getAbsolutePath().replace(settings.getDownloadPath(), "")); - dataService.updatePostDownloadDirectory(post.getDownloadDirectory(), post.getId()); - } - - public final void rename(@NonNull String postId, @NonNull String altName, Settings settings) - throws RenameException { - Post post = dataService.findPostByPostId(postId).orElseThrow(); - if (altName.equals(post.getTitle())) { - return; - } - - // Download have not started yet - if (post.getDownloadDirectory() == null) { - post.setTitle(altName); - dataService.updatePostTitle(post.getTitle(), post.getId()); - return; - } - - File newDownloadDirectory = getRootFolder(post.getForum(), post.getThreadTitle(), settings); - newDownloadDirectory = new File(newDownloadDirectory, sanitize(altName)); - File currentDownloadDirectory = calcDownloadDirectory(post, settings); - try { - directoryAccess.lock(); - Files.move( - currentDownloadDirectory.toPath(), - newDownloadDirectory.toPath(), - StandardCopyOption.ATOMIC_MOVE); - post.setDownloadDirectory( - newDownloadDirectory.getAbsolutePath().replace(settings.getDownloadPath(), "")); - dataService.updatePostDownloadDirectory(post.getDownloadDirectory(), post.getId()); - - post.setTitle(altName); - dataService.updatePostTitle(post.getTitle(), post.getId()); - } catch (IOException e) { - throw new RenameException( - String.format( - "Failed to move files from %s to %s", currentDownloadDirectory, newDownloadDirectory), - e); - } finally { - directoryAccess.unlock(); - } - } - - private File makeDir(@NonNull final File sourceFolder) { - int counter = 1; - File folder = sourceFolder; - - while (folder.exists()) { - folder = new File(sourceFolder.toString() + '.' + counter++); - } - - if (!folder.mkdirs()) { - throw new RuntimeException(String.format("Failed to create the folder %s", sourceFolder)); - } - - return folder; - } - - private String sanitize(final String folderName) { - String sanitizedFolderName = - folderName.replaceAll("\\.|\\\\|/|\\||:|\\?|\\*|\"|<|>|\\p{Cntrl}", "_"); - log.debug(String.format("%s sanitized to %s", folderName, sanitizedFolderName)); - return sanitizedFolderName; - } - - /** - * Will sanitize the image name and remove extension - * - * @param imageName path string - * @return Sanitized local path string - */ - public final String formatImageFileName(String imageName) { - return sanitize(imageName); - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/services/PostService.java b/vripper-server/src/main/java/tn/mnlr/vripper/services/PostService.java deleted file mode 100644 index 66abf465..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/services/PostService.java +++ /dev/null @@ -1,73 +0,0 @@ -package tn.mnlr.vripper.services; - -import com.github.benmanes.caffeine.cache.Caffeine; -import com.github.benmanes.caffeine.cache.LoadingCache; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import reactor.core.Disposable; -import tn.mnlr.vripper.event.Event; -import tn.mnlr.vripper.event.EventBus; -import tn.mnlr.vripper.jpa.domain.Queued; -import tn.mnlr.vripper.services.domain.MultiPostScanParser; -import tn.mnlr.vripper.services.domain.MultiPostScanResult; -import tn.mnlr.vripper.tasks.AddPostRunnable; -import tn.mnlr.vripper.tasks.AddQueuedRunnable; - -import javax.annotation.PreDestroy; -import java.util.List; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; - -@Service -@Slf4j -public class PostService { - - private final DataService dataService; - private final ThreadPoolService threadPoolService; - private final LoadingCache cache; - private final Disposable disposable; - - @Autowired - public PostService( - DataService dataService, ThreadPoolService threadPoolService, EventBus eventBus) { - this.dataService = dataService; - this.threadPoolService = threadPoolService; - cache = - Caffeine.newBuilder() - .expireAfterWrite(5, TimeUnit.MINUTES) - .build(multiPostItem -> new MultiPostScanParser(multiPostItem).parse()); - disposable = - eventBus - .flux() - .filter(e -> e.getKind().equals(Event.Kind.SETTINGS_UPDATE)) - .subscribe(e -> this.cache.invalidateAll()); - } - - @PreDestroy - private void destroy() { - if (disposable != null) { - disposable.dispose(); - } - } - - public void processMultiPost(List queuedList) { - for (Queued queued : queuedList) { - if (queued.getPostId() != null) { - threadPoolService - .getGeneralExecutor() - .submit(new AddPostRunnable(queued.getPostId(), queued.getThreadId())); - } else { - threadPoolService.getGeneralExecutor().submit(new AddQueuedRunnable(queued)); - } - } - } - - public MultiPostScanResult get(Queued queued) throws ExecutionException { - return cache.get(queued); - } - - public void remove(String threadId) { - dataService.removeQueueLink(threadId); - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/services/SettingsService.java b/vripper-server/src/main/java/tn/mnlr/vripper/services/SettingsService.java deleted file mode 100644 index 25a3c7e5..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/services/SettingsService.java +++ /dev/null @@ -1,305 +0,0 @@ -package tn.mnlr.vripper.services; - -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.DeserializationFeature; -import com.fasterxml.jackson.databind.ObjectMapper; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.codec.digest.DigestUtils; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.core.io.Resource; -import org.springframework.stereotype.Service; -import tn.mnlr.vripper.SpringContext; -import tn.mnlr.vripper.event.Event; -import tn.mnlr.vripper.event.EventBus; -import tn.mnlr.vripper.exception.ValidationException; -import tn.mnlr.vripper.services.domain.Settings; - -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; -import java.io.FileWriter; -import java.io.IOException; -import java.io.InputStream; -import java.nio.file.Files; -import java.nio.file.InvalidPathException; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import static java.nio.file.StandardOpenOption.*; - -@Service -@Setter -@Slf4j -public class SettingsService { - - private final Path configPath; - private final Path customProxiesPath; - private final ObjectMapper om = new ObjectMapper(); - - private final Set proxies = new HashSet<>(); - - private final EventBus eventBus; - - @Getter private Settings settings = new Settings(); - - @Value("classpath:proxies.json") - private Resource defaultProxies; - - public SettingsService( - @Value("${base.dir}") String baseDir, - @Value("${base.dir.name}") String baseDirName, - EventBus eventBus) { - this.eventBus = eventBus; - this.configPath = Paths.get(baseDir, baseDirName, "config.json"); - this.customProxiesPath = Paths.get(baseDir, baseDirName, "proxies.json"); - om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); - } - - @PostConstruct - private void init() { - loadViperProxies(); - restore(); - eventBus.publishEvent(Event.wrap(Event.Kind.SETTINGS_UPDATE, settings)); - } - - private void loadViperProxies() { - try (InputStream defaultProxiesIs = defaultProxies.getInputStream()) { - List defaultProxies = om.readValue(defaultProxiesIs, new TypeReference<>() {}); - List customProxies = new ArrayList<>(); - if (customProxiesPath.toFile().exists() && customProxiesPath.toFile().isFile()) { - customProxies = om.readValue(customProxiesPath.toFile(), new TypeReference<>() {}); - } else { - if (!customProxiesPath.toFile().createNewFile()) { - log.warn("Unable to create " + customProxiesPath.toFile().getAbsolutePath()); - } else { - try (FileWriter fw = new FileWriter(customProxiesPath.toFile())) { - fw.append("[]").append(System.lineSeparator()); - fw.flush(); - } catch (IOException e) { - log.warn("Unable to create " + customProxiesPath.toFile().getAbsolutePath(), e); - } - } - } - proxies.addAll(customProxies); - proxies.addAll(defaultProxies); - } catch (IOException e) { - log.error("Failed to load vipergirls proxies list", e); - } - proxies.add("https://vipergirls.to"); - } - - public List getProxies() { - return new ArrayList<>(proxies); - } - - public void newSettings(Settings settings) { - - if (settings.getVLogin() != null && settings.getVLogin()) { - if (!this.settings.getVPassword().equals(settings.getVPassword())) { - settings.setVPassword(DigestUtils.md5Hex(settings.getVPassword())); - } - } else { - settings.setVUsername(""); - settings.setVPassword(""); - settings.setVThanks(false); - settings.setVLogin(false); - } - this.settings = settings; - - save(); - eventBus.publishEvent(Event.wrap(Event.Kind.SETTINGS_UPDATE, settings)); - } - - public void restore() { - try { - if (configPath.toFile().exists()) { - settings = om.readValue(configPath.toFile(), Settings.class); - } - } catch (IOException e) { - log.error("Failed restore user settings", e); - settings = new Settings(); - } - - if (settings.getDownloadPath() == null) { - settings.setDownloadPath(System.getProperty("user.home")); - } - - if (settings.getMaxThreads() == null) { - settings.setMaxThreads(4); - } - - if (settings.getMaxTotalThreads() == null) { - settings.setMaxTotalThreads(0); - } - - if (settings.getAutoStart() == null) { - settings.setAutoStart(true); - } - - if (settings.getVLogin() == null) { - settings.setVLogin(false); - } - - if (settings.getVUsername() == null) { - settings.setVUsername(""); - } - - if (settings.getVPassword() == null) { - settings.setVPassword(""); - } - - if (settings.getVThanks() == null) { - settings.setVThanks(false); - } - - if (settings.getDesktopClipboard() == null) { - settings.setDesktopClipboard(false); - } - - if (settings.getForceOrder() == null) { - settings.setForceOrder(false); - } - - if (settings.getSubLocation() == null) { - settings.setSubLocation(false); - } - - if (settings.getThreadSubLocation() == null) { - settings.setThreadSubLocation(false); - } - - if (settings.getClearCompleted() == null) { - settings.setClearCompleted(false); - } - - if (settings.getDarkTheme() == null) { - settings.setDarkTheme(false); - } - - if (settings.getAppendPostId() == null) { - settings.setAppendPostId(true); - } - - if (settings.getLeaveThanksOnStart() == null) { - settings.setLeaveThanksOnStart(false); - } - - if (settings.getConnectionTimeout() == null) { - settings.setConnectionTimeout(30); - } - - if (settings.getMaxAttempts() == null) { - settings.setMaxAttempts(5); - } - - if (settings.getVProxy() == null || !proxies.contains(settings.getVProxy())) { - settings.setVProxy("https://vipergirls.to"); - } - - if (settings.getMaxEventLog() == null) { - settings.setMaxEventLog(1000); - } - - try { - check(this.settings); - } catch (ValidationException e) { - log.error( - String.format("Your settings are invalid, either remove %s, or fix it", configPath), e); - SpringContext.close(); - } - - save(); - } - - public void save() { - try { - Files.write( - configPath, om.writeValueAsBytes(settings), CREATE, WRITE, TRUNCATE_EXISTING, SYNC); - } catch (IOException e) { - log.error("Failed to store user settings", e); - } - } - - @PreDestroy - private void destroy() { - save(); - } - - public void check(Settings settings) throws ValidationException { - - Path path; - try { - path = Paths.get(settings.getDownloadPath()); - } catch (InvalidPathException e) { - throw new ValidationException(String.format("%s is invalid", settings.getDownloadPath())); - } - if (!Files.exists(path)) { - throw new ValidationException(String.format("%s does not exist", settings.getDownloadPath())); - } else if (!Files.isDirectory(path)) { - throw new ValidationException( - String.format("%s is not a directory", settings.getDownloadPath())); - } - - if (settings.getMaxTotalThreads() < 0 || settings.getMaxTotalThreads() > 12) { - throw new ValidationException( - String.format( - "Invalid max global concurrent download settings, values must be in [%d,%d]", 0, 12)); - } - - if (settings.getMaxThreads() < 1 || settings.getMaxThreads() > 4) { - throw new ValidationException( - String.format( - "Invalid max concurrent download settings, values must be in [%d,%d]", 1, 4)); - } - - if (settings.getConnectionTimeout() < 1 || settings.getConnectionTimeout() > 300) { - throw new ValidationException( - String.format("Invalid connection timeout settings, values must be in [%d,%d]", 1, 300)); - } - - if (settings.getMaxAttempts() < 1 || settings.getMaxAttempts() > 10) { - throw new ValidationException( - String.format("Invalid maximum attempts settings, values must be in [%d,%d]", 1, 10)); - } - - if (settings.getMaxEventLog() < 100 || settings.getMaxEventLog() > 10_000) { - throw new ValidationException( - String.format( - "Invalid maximum event log record settings, values must be in [%d,%d]", 100, 10_000)); - } - - if (!settings.getVThanks() - && settings.getLeaveThanksOnStart() != null - && settings.getLeaveThanksOnStart()) { - throw new ValidationException("Invalid value, leave thanks must be checked"); - } - } - - public Theme getTheme() { - return new Theme(this.settings.getDarkTheme()); - } - - public void setTheme(Theme theme) { - this.settings.setDarkTheme(theme.darkTheme); - save(); - } - - @Getter - @NoArgsConstructor - public static class Theme { - - @JsonProperty("darkTheme") - private boolean darkTheme; - - Theme(boolean darkTheme) { - this.darkTheme = darkTheme; - } - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/services/ThreadPoolService.java b/vripper-server/src/main/java/tn/mnlr/vripper/services/ThreadPoolService.java deleted file mode 100644 index 90e341fc..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/services/ThreadPoolService.java +++ /dev/null @@ -1,20 +0,0 @@ -package tn.mnlr.vripper.services; - -import lombok.Getter; -import org.springframework.stereotype.Service; - -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; - -@Service -public class ThreadPoolService { - @Getter - private final ExecutorService generalExecutor = - Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); - - public void destroy() throws Exception { - generalExecutor.shutdown(); - generalExecutor.awaitTermination(5, TimeUnit.SECONDS); - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/services/VGAuthService.java b/vripper-server/src/main/java/tn/mnlr/vripper/services/VGAuthService.java deleted file mode 100644 index b6ddc67d..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/services/VGAuthService.java +++ /dev/null @@ -1,159 +0,0 @@ -package tn.mnlr.vripper.services; - -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; -import org.apache.http.NameValuePair; -import org.apache.http.client.entity.UrlEncodedFormEntity; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.protocol.HttpClientContext; -import org.apache.http.cookie.Cookie; -import org.apache.http.impl.client.BasicCookieStore; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.message.BasicNameValuePair; -import org.apache.http.util.EntityUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import reactor.core.Disposable; -import tn.mnlr.vripper.event.Event; -import tn.mnlr.vripper.event.EventBus; -import tn.mnlr.vripper.exception.VripperException; -import tn.mnlr.vripper.jpa.domain.Post; -import tn.mnlr.vripper.tasks.LeaveThanksRunnable; - -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; -import java.util.ArrayList; -import java.util.List; - -@Service -@Slf4j -public class VGAuthService { - - private final ConnectionService cm; - private final SettingsService settingsService; - private final ThreadPoolService threadPoolService; - - private final Disposable disposable; - - @Getter private final HttpClientContext context = HttpClientContext.create(); - @Getter private boolean authenticated = false; - @Getter private String loggedUser = ""; - - private final EventBus eventBus; - - @Autowired - public VGAuthService( - ConnectionService cm, - SettingsService settingsService, - ThreadPoolService threadPoolService, - EventBus eventBus) { - this.cm = cm; - this.settingsService = settingsService; - this.threadPoolService = threadPoolService; - this.eventBus = eventBus; - disposable = - eventBus - .flux() - .filter(p -> p.getKind().equals(Event.Kind.SETTINGS_UPDATE)) - .subscribe(e -> authenticate()); - } - - @PostConstruct - private void init() { - context.setCookieStore(new BasicCookieStore()); - authenticate(); - } - - @PreDestroy - private void destroy() { - disposable.dispose(); - } - - public void authenticate() { - - log.info("Authenticating using ViperGirls credentials"); - authenticated = false; - - if (!settingsService.getSettings().getVLogin()) { - log.debug("Authentication option is disabled"); - context.getCookieStore().clear(); - loggedUser = ""; - eventBus.publishEvent(Event.wrap(Event.Kind.VG_USER, loggedUser)); - return; - } - - String username = settingsService.getSettings().getVUsername(); - String password = settingsService.getSettings().getVPassword(); - - if (username == null || password == null || username.isEmpty() || password.isEmpty()) { - log.error("Cannot authenticate with ViperGirls credentials, username or password is empty"); - context.getCookieStore().clear(); - loggedUser = ""; - eventBus.publishEvent(Event.wrap(Event.Kind.VG_USER, loggedUser)); - return; - } - - HttpPost postAuth = - cm.buildHttpPost(settingsService.getSettings().getVProxy() + "/login.php?do=login", null); - List params = new ArrayList<>(); - params.add(new BasicNameValuePair("vb_login_username", username)); - - params.add(new BasicNameValuePair("cookieuser", "1")); - params.add(new BasicNameValuePair("do", "login")); - params.add(new BasicNameValuePair("vb_login_md5password", password)); - try { - postAuth.setEntity(new UrlEncodedFormEntity(params)); - } catch (Exception e) { - context.getCookieStore().clear(); - loggedUser = ""; - eventBus.publishEvent(Event.wrap(Event.Kind.VG_USER, loggedUser)); - log.error("Failed to authenticate user with " + settingsService.getSettings().getVProxy(), e); - return; - } - - postAuth.addHeader("Referer", settingsService.getSettings().getVProxy()); - postAuth.addHeader( - "Host", - settingsService.getSettings().getVProxy().replace("https://", "").replace("http://", "")); - - CloseableHttpClient client = cm.getClient().build(); - - try (CloseableHttpResponse response = client.execute(postAuth, context)) { - - if (response.getStatusLine().getStatusCode() / 100 != 2) { - throw new VripperException( - String.format( - "Unexpected response code returned %s", response.getStatusLine().getStatusCode())); - } - String responseBody = EntityUtils.toString(response.getEntity()); - log.debug(String.format("Authentication with ViperGirls response body:%n%s", responseBody)); - EntityUtils.consumeQuietly(response.getEntity()); - if (context.getCookieStore().getCookies().stream() - .map(Cookie::getName) - .noneMatch(e -> e.equals("vg_userid"))) { - log.error( - String.format( - "Failed to authenticate user with %s, missing vg_userid cookie", - settingsService.getSettings().getVProxy())); - return; - } - } catch (Exception e) { - context.getCookieStore().clear(); - loggedUser = ""; - eventBus.publishEvent(Event.wrap(Event.Kind.VG_USER, loggedUser)); - log.error("Failed to authenticate user with " + settingsService.getSettings().getVProxy(), e); - return; - } - authenticated = true; - loggedUser = username; - log.info(String.format("Authenticated: %s", username)); - eventBus.publishEvent(Event.wrap(Event.Kind.VG_USER, loggedUser)); - } - - public void leaveThanks(Post post) { - threadPoolService - .getGeneralExecutor() - .submit(new LeaveThanksRunnable(post, authenticated, context)); - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/services/XpathService.java b/vripper-server/src/main/java/tn/mnlr/vripper/services/XpathService.java deleted file mode 100644 index 8c045e81..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/services/XpathService.java +++ /dev/null @@ -1,32 +0,0 @@ -package tn.mnlr.vripper.services; - -import org.springframework.stereotype.Service; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; -import tn.mnlr.vripper.exception.XpathException; - -import javax.xml.xpath.XPath; -import javax.xml.xpath.XPathConstants; -import javax.xml.xpath.XPathFactory; - -@Service -public class XpathService { - - private final XPath xpath = XPathFactory.newInstance().newXPath(); - - public Node getAsNode(Node source, String xpathExpression) throws XpathException { - try { - return (Node) xpath.compile(xpathExpression).evaluate(source, XPathConstants.NODE); - } catch (Exception e) { - throw new XpathException(e); - } - } - - public NodeList getAsNodeList(Node source, String xpathExpression) throws XpathException { - try { - return (NodeList) xpath.compile(xpathExpression).evaluate(source, XPathConstants.NODESET); - } catch (Exception e) { - throw new XpathException(e); - } - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/services/domain/DownloadSpeed.java b/vripper-server/src/main/java/tn/mnlr/vripper/services/domain/DownloadSpeed.java deleted file mode 100644 index e8aa763d..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/services/domain/DownloadSpeed.java +++ /dev/null @@ -1,40 +0,0 @@ -package tn.mnlr.vripper.services.domain; - -import lombok.Getter; - -import java.util.Objects; - -@Getter -public class DownloadSpeed { - - private final String speed; - - public DownloadSpeed(long bytes) { - speed = formatSI(bytes); - } - - private String formatSI(long bytes) { - return humanReadableByteCount(bytes, false); - } - - private String humanReadableByteCount(long bytes, boolean si) { - int unit = si ? 1000 : 1024; - if (bytes < unit) return bytes + " B"; - int exp = (int) (Math.log(bytes) / Math.log(unit)); - String pre = (si ? "kMGTPE" : "KMGTPE").charAt(exp - 1) + (si ? "" : "i"); - return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - DownloadSpeed that = (DownloadSpeed) o; - return Objects.equals(speed, that.speed); - } - - @Override - public int hashCode() { - return Objects.hash(speed); - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/services/domain/GlobalState.java b/vripper-server/src/main/java/tn/mnlr/vripper/services/domain/GlobalState.java deleted file mode 100644 index dbdbd1ad..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/services/domain/GlobalState.java +++ /dev/null @@ -1,32 +0,0 @@ -package tn.mnlr.vripper.services.domain; - -import lombok.Getter; - -import java.util.Objects; - -@Getter -public class GlobalState { - - private final long running; - private final long remaining; - private final long error; - - public GlobalState(long running, long remaining, long error) { - this.running = running; - this.remaining = remaining; - this.error = error; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - GlobalState that = (GlobalState) o; - return running == that.running && remaining == that.remaining && error == that.error; - } - - @Override - public int hashCode() { - return Objects.hash(running, remaining, error); - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/services/domain/MultiPostItem.java b/vripper-server/src/main/java/tn/mnlr/vripper/services/domain/MultiPostItem.java deleted file mode 100644 index eaebdd50..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/services/domain/MultiPostItem.java +++ /dev/null @@ -1,36 +0,0 @@ -package tn.mnlr.vripper.services.domain; - -import lombok.Getter; - -import java.util.List; - -@Getter -public class MultiPostItem { - private final String threadId; - private final String postId; - private final int number; - private final String title; - private final int imageCount; - private final String url; - private final List previews; - private final String hosts; - - public MultiPostItem( - String threadId, - String postId, - int number, - String title, - int imageCount, - String url, - List previews, - String hosts) { - this.threadId = threadId; - this.postId = postId; - this.number = number; - this.title = title; - this.imageCount = imageCount; - this.previews = previews; - this.url = url; - this.hosts = hosts; - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/services/domain/MultiPostScanHandler.java b/vripper-server/src/main/java/tn/mnlr/vripper/services/domain/MultiPostScanHandler.java deleted file mode 100644 index 61123460..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/services/domain/MultiPostScanHandler.java +++ /dev/null @@ -1,114 +0,0 @@ -package tn.mnlr.vripper.services.domain; - -import org.xml.sax.Attributes; -import org.xml.sax.helpers.DefaultHandler; -import tn.mnlr.vripper.SpringContext; -import tn.mnlr.vripper.host.Host; -import tn.mnlr.vripper.jpa.domain.Queued; -import tn.mnlr.vripper.services.SettingsService; - -import java.util.*; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.stream.Collectors; - -public class MultiPostScanHandler extends DefaultHandler { - - private final Queued queued; - private final Collection supportedHosts; - private final SettingsService settingsService; - private final Map hostMap = new HashMap<>(); - private final List posts = new ArrayList<>(); - private String error; - private List previews = new ArrayList<>(); - private String threadTitle; - private String postId; - private String postTitle; - private int imageCount; - private int postCounter; - private int previewCounter = 0; - - public MultiPostScanHandler(Queued queued) { - this.queued = queued; - supportedHosts = SpringContext.getBeansOfType(Host.class).values(); - settingsService = SpringContext.getBean(SettingsService.class); - } - - public MultiPostScanResult getScanResult() { - return new MultiPostScanResult(posts, error); - } - - @Override - public void startDocument() {} - - @Override - public void startElement(String uri, String localName, String qName, Attributes attributes) { - - switch (qName.toLowerCase()) { - case "error": - error = attributes.getValue("details"); - break; - case "thread": - threadTitle = attributes.getValue("title").trim(); - break; - case "post": - imageCount = Integer.parseInt(attributes.getValue("imagecount").trim()); - postId = attributes.getValue("id").trim(); - postCounter = Integer.parseInt(attributes.getValue("number").trim()); - postTitle = - Optional.ofNullable(attributes.getValue("title")) - .map(e -> e.trim().isEmpty() ? null : e.trim()) - .orElse(threadTitle); - break; - case "image": - Optional.ofNullable(attributes.getValue("main_url")) - .map(String::trim) - .flatMap( - mainUrl -> - supportedHosts.stream().filter(host -> host.isSupported(mainUrl)).findFirst()) - .ifPresent( - host -> - Optional.ofNullable(hostMap.get(host)) - .ifPresentOrElse( - AtomicInteger::incrementAndGet, - () -> hostMap.put(host, new AtomicInteger(1)))); - if (previewCounter++ < 4) { - Optional.ofNullable(attributes.getValue("thumb_url")) - .map(String::trim) - .ifPresent(previews::add); - } - break; - } - } - - @Override - public void endElement(String uri, String localName, String qName) { - if ("post".equalsIgnoreCase(qName)) { - if (imageCount != 0) { - posts.add( - new MultiPostItem( - queued.getThreadId(), - postId, - postCounter, - postTitle, - imageCount, - String.format( - "%s/threads/?p=%s&viewfull=1#post%s", - settingsService.getSettings().getVProxy(), postId, postId), - previews, - hostMap.entrySet().stream() - .filter(v -> v.getValue().get() > 0) - .map(e -> e.getKey().getHost() + " (" + e.getValue().get() + ")") - .collect(Collectors.joining(", ")))); - queued.increment(); - } - previewCounter = 0; - previews = new ArrayList<>(); - hostMap.clear(); - } - } - - @Override - public void endDocument() { - queued.done(); - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/services/domain/MultiPostScanParser.java b/vripper-server/src/main/java/tn/mnlr/vripper/services/domain/MultiPostScanParser.java deleted file mode 100644 index 9cd1ba69..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/services/domain/MultiPostScanParser.java +++ /dev/null @@ -1,91 +0,0 @@ -package tn.mnlr.vripper.services.domain; - -import lombok.extern.slf4j.Slf4j; -import net.jodah.failsafe.Failsafe; -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.utils.URIBuilder; -import org.apache.http.util.EntityUtils; -import tn.mnlr.vripper.SpringContext; -import tn.mnlr.vripper.exception.DownloadException; -import tn.mnlr.vripper.exception.PostParseException; -import tn.mnlr.vripper.jpa.domain.Queued; -import tn.mnlr.vripper.services.ConnectionService; -import tn.mnlr.vripper.services.SettingsService; -import tn.mnlr.vripper.services.VGAuthService; - -import javax.xml.parsers.SAXParserFactory; -import java.io.BufferedInputStream; -import java.net.URISyntaxException; -import java.util.concurrent.atomic.AtomicReference; - -@Slf4j -public class MultiPostScanParser { - - private static final SAXParserFactory factory = SAXParserFactory.newInstance(); - private final Queued queued; - private final ConnectionService cm; - private final VGAuthService VGAuthService; - private final SettingsService settingsService; - - public MultiPostScanParser(Queued queued) { - this.queued = queued; - cm = SpringContext.getBean(ConnectionService.class); - VGAuthService = SpringContext.getBean(VGAuthService.class); - settingsService = SpringContext.getBean(SettingsService.class); - } - - public MultiPostScanResult parse() throws PostParseException { - - log.debug(String.format("Parsing thread %s", queued)); - HttpGet httpGet; - try { - URIBuilder uriBuilder = new URIBuilder(settingsService.getSettings().getVProxy() + "/vr.php"); - uriBuilder.setParameter("t", queued.getThreadId()); - httpGet = cm.buildHttpGet(uriBuilder.build(), null); - } catch (URISyntaxException e) { - throw new PostParseException(e); - } - - MultiPostScanHandler multiPostScanHandler = new MultiPostScanHandler(queued); - AtomicReference thr = new AtomicReference<>(); - log.debug(String.format("Requesting %s", httpGet)); - MultiPostScanResult multiPostScanResult = - Failsafe.with(cm.getRetryPolicy()) - .onFailure(e -> thr.set(e.getFailure())) - .get( - () -> { - HttpClient connection = cm.getClient().build(); - try (CloseableHttpResponse response = - (CloseableHttpResponse) - connection.execute(httpGet, VGAuthService.getContext())) { - if (response.getStatusLine().getStatusCode() / 100 != 2) { - throw new DownloadException( - String.format( - "Unexpected response code '%d' for %s", - response.getStatusLine().getStatusCode(), httpGet)); - } - - try { - factory - .newSAXParser() - .parse( - new BufferedInputStream(response.getEntity().getContent()), - multiPostScanHandler); - return multiPostScanHandler.getScanResult(); - } catch (Exception e) { - throw new PostParseException( - String.format("Failed to parse thread %s", queued), e); - } finally { - EntityUtils.consumeQuietly(response.getEntity()); - } - } - }); - if (thr.get() != null) { - log.error(String.format("parsing failed for thread %s", queued), thr.get()); - throw new PostParseException(thr.get()); - } - return multiPostScanResult; - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/services/domain/MultiPostScanResult.java b/vripper-server/src/main/java/tn/mnlr/vripper/services/domain/MultiPostScanResult.java deleted file mode 100644 index 127bf054..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/services/domain/MultiPostScanResult.java +++ /dev/null @@ -1,16 +0,0 @@ -package tn.mnlr.vripper.services.domain; - -import lombok.Getter; - -import java.util.List; - -@Getter -public class MultiPostScanResult { - private final List posts; - private final String error; - - public MultiPostScanResult(List posts, String error) { - this.posts = posts; - this.error = error; - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/services/domain/PostScanHandler.java b/vripper-server/src/main/java/tn/mnlr/vripper/services/domain/PostScanHandler.java deleted file mode 100644 index c8d48b64..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/services/domain/PostScanHandler.java +++ /dev/null @@ -1,116 +0,0 @@ -package tn.mnlr.vripper.services.domain; - -import lombok.extern.slf4j.Slf4j; -import org.xml.sax.Attributes; -import org.xml.sax.helpers.DefaultHandler; -import tn.mnlr.vripper.SpringContext; -import tn.mnlr.vripper.host.Host; -import tn.mnlr.vripper.jpa.domain.Image; -import tn.mnlr.vripper.jpa.domain.Post; -import tn.mnlr.vripper.services.SettingsService; - -import java.util.Collection; -import java.util.HashSet; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; - -@Slf4j -class PostScanHandler extends DefaultHandler { - - private final Collection supportedHosts; - - private final String threadId; - private final String postId; - private final String postUrl; - private final Set images = new HashSet<>(); - private Set previews = new HashSet<>(); - private String threadTitle; - private String forum; - private String userHash; - private int index = 0; - - private Post parsedPost; - - PostScanHandler(String threadId, String postId) { - this.threadId = threadId; - this.postId = postId; - supportedHosts = SpringContext.getBeansOfType(Host.class).values(); - SettingsService settingsService = SpringContext.getBean(SettingsService.class); - postUrl = - String.format( - "%s/threads/%s/?p=%s&viewfull=1#post%s", - settingsService.getSettings().getVProxy(), this.threadId, this.postId, this.postId); - } - - public PostScanResult getParsedPost() { - return new PostScanResult(parsedPost, images); - } - - @Override - public void startDocument() {} - - @Override - public void startElement(String uri, String localName, String qName, Attributes attributes) { - - switch (qName.toLowerCase()) { - case "forum": - forum = attributes.getValue("title").trim(); - break; - case "user": - userHash = attributes.getValue("hash").trim(); - break; - case "thread": - threadTitle = attributes.getValue("title").trim(); - break; - case "post": - String postTitle = - Optional.ofNullable(attributes.getValue("title")) - .map(e -> e.trim().isEmpty() ? null : e.trim()) - .orElse(threadTitle); - parsedPost = new Post(postTitle, postUrl, postId, threadId, threadTitle, forum, userHash); - break; - case "image": - index++; - if (previews.size() < 4) { - Optional.ofNullable(attributes.getValue("thumb_url")) - .map(String::trim) - .ifPresent(previews::add); - } - - String mainUrl = - Optional.ofNullable(attributes.getValue("main_url")).map(String::trim).orElse(null); - if (mainUrl != null) { - Host foundHost = - supportedHosts.stream() - .filter(host -> host.isSupported(mainUrl)) - .findFirst() - .orElse(null); - if (foundHost != null) { - log.debug( - String.format( - "Found supported host %s for %s", - foundHost.getClass().getSimpleName(), mainUrl)); - images.add(new Image(postId, mainUrl, foundHost, index)); - } else { - log.warn(String.format("unsupported host for %s, skipping", mainUrl)); - } - } - break; - } - } - - @Override - public void endElement(String uri, String localName, String qName) { - if ("post".equalsIgnoreCase(qName)) { - parsedPost.setTotal(images.size()); - if (!previews.isEmpty()) { - parsedPost.setPreviews(previews); - } - parsedPost.setHosts( - images.stream().map(Image::getHost).map(Host::getHost).collect(Collectors.toSet())); - index = 0; - previews = new HashSet<>(); - } - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/services/domain/PostScanParser.java b/vripper-server/src/main/java/tn/mnlr/vripper/services/domain/PostScanParser.java deleted file mode 100644 index e484da05..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/services/domain/PostScanParser.java +++ /dev/null @@ -1,86 +0,0 @@ -package tn.mnlr.vripper.services.domain; - -import lombok.extern.slf4j.Slf4j; -import net.jodah.failsafe.Failsafe; -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.utils.URIBuilder; -import org.apache.http.util.EntityUtils; -import tn.mnlr.vripper.SpringContext; -import tn.mnlr.vripper.exception.DownloadException; -import tn.mnlr.vripper.exception.PostParseException; -import tn.mnlr.vripper.services.ConnectionService; -import tn.mnlr.vripper.services.SettingsService; -import tn.mnlr.vripper.services.VGAuthService; - -import javax.xml.parsers.SAXParserFactory; -import java.net.URISyntaxException; -import java.util.concurrent.atomic.AtomicReference; - -@Slf4j -public class PostScanParser { - - private static final SAXParserFactory factory = SAXParserFactory.newInstance(); - - private final String threadId; - private final String postId; - private final ConnectionService cm; - private final VGAuthService VGAuthService; - private final SettingsService settingsService; - - public PostScanParser(String threadId, String postId) { - this.threadId = threadId; - this.postId = postId; - cm = SpringContext.getBean(ConnectionService.class); - VGAuthService = SpringContext.getBean(VGAuthService.class); - settingsService = SpringContext.getBean(SettingsService.class); - } - - public PostScanResult parse() throws PostParseException { - - log.debug(String.format("Parsing post %s", postId)); - HttpGet httpGet; - try { - URIBuilder uriBuilder = new URIBuilder(settingsService.getSettings().getVProxy() + "/vr.php"); - uriBuilder.setParameter("p", postId); - httpGet = cm.buildHttpGet(uriBuilder.build(), null); - } catch (URISyntaxException e) { - throw new PostParseException(e); - } - - AtomicReference thr = new AtomicReference<>(); - PostScanHandler postScanHandler = new PostScanHandler(threadId, postId); - log.debug(String.format("Requesting %s", httpGet)); - PostScanResult post = getPost(httpGet, postScanHandler, thr); - if (thr.get() != null) { - log.error( - String.format("parsing failed for thread %s, post %s", threadId, postId), thr.get()); - throw new PostParseException(thr.get()); - } - return post; - } - - private PostScanResult getPost( - HttpGet httpGet, PostScanHandler postScanHandler, AtomicReference thr) { - return Failsafe.with(cm.getRetryPolicy()) - .onFailure(e -> thr.set(e.getFailure())) - .get( - () -> { - HttpClient connection = cm.getClient().build(); - try (CloseableHttpResponse response = - (CloseableHttpResponse) connection.execute(httpGet, VGAuthService.getContext())) { - if (response.getStatusLine().getStatusCode() / 100 != 2) { - throw new DownloadException( - String.format( - "Unexpected response code '%d' for %s", - response.getStatusLine().getStatusCode(), httpGet)); - } - - factory.newSAXParser().parse(response.getEntity().getContent(), postScanHandler); - EntityUtils.consumeQuietly(response.getEntity()); - return postScanHandler.getParsedPost(); - } - }); - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/services/domain/PostScanResult.java b/vripper-server/src/main/java/tn/mnlr/vripper/services/domain/PostScanResult.java deleted file mode 100644 index 5ccb8b53..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/services/domain/PostScanResult.java +++ /dev/null @@ -1,24 +0,0 @@ -package tn.mnlr.vripper.services.domain; - -import lombok.Getter; -import tn.mnlr.vripper.jpa.domain.Image; -import tn.mnlr.vripper.jpa.domain.Post; - -import java.util.Optional; -import java.util.Set; - -public class PostScanResult { - - private final Post post; - - @Getter private final Set images; - - public PostScanResult(Post post, Set images) { - this.post = post; - this.images = images; - } - - public Optional getPost() { - return Optional.ofNullable(post); - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/services/domain/Settings.java b/vripper-server/src/main/java/tn/mnlr/vripper/services/domain/Settings.java deleted file mode 100644 index e2fa809b..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/services/domain/Settings.java +++ /dev/null @@ -1,108 +0,0 @@ -package tn.mnlr.vripper.services.domain; - -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.extern.slf4j.Slf4j; - -@Getter -@Setter -@Slf4j -@NoArgsConstructor -@JsonInclude(JsonInclude.Include.NON_NULL) -public class Settings implements Cloneable { - - @JsonProperty("downloadPath") - private String downloadPath; - - @JsonProperty("maxThreads") - private Integer maxThreads; - - @JsonProperty("maxTotalThreads") - private Integer maxTotalThreads; - - @JsonProperty("autoStart") - private Boolean autoStart; - - @JsonProperty("vLogin") - private Boolean vLogin; - - @JsonProperty("vUsername") - private String vUsername; - - @JsonProperty("vPassword") - private String vPassword; - - @JsonProperty("vThanks") - private Boolean vThanks; - - @JsonProperty("desktopClipboard") - private Boolean desktopClipboard; - - @JsonProperty("forceOrder") - private Boolean forceOrder; - - @JsonProperty("subLocation") - private Boolean subLocation; - - @JsonProperty("threadSubLocation") - private Boolean threadSubLocation; - - @JsonProperty("clearCompleted") - private Boolean clearCompleted; - - @JsonProperty("darkTheme") - private Boolean darkTheme; - - @JsonProperty("appendPostId") - private Boolean appendPostId; - - @JsonProperty("leaveThanksOnStart") - private Boolean leaveThanksOnStart; - - @JsonProperty("connectionTimeout") - private Integer connectionTimeout; - - @JsonProperty("maxAttempts") - private Integer maxAttempts; - - @JsonProperty("vProxy") - private String vProxy; - - @JsonProperty("maxEventLog") - private Integer maxEventLog; - - @Override - public Object clone() { - Settings clone; - try { - clone = (Settings) super.clone(); - } catch (CloneNotSupportedException e) { - log.error(e.getMessage(), e); - clone = new Settings(); - } - clone.downloadPath = downloadPath; - clone.maxThreads = maxThreads; - clone.maxTotalThreads = maxTotalThreads; - clone.autoStart = autoStart; - clone.vLogin = vLogin; - clone.vUsername = vUsername; - clone.vPassword = vPassword; - clone.vThanks = vThanks; - clone.desktopClipboard = desktopClipboard; - clone.forceOrder = forceOrder; - clone.subLocation = subLocation; - clone.threadSubLocation = threadSubLocation; - clone.clearCompleted = clearCompleted; - clone.darkTheme = darkTheme; - clone.appendPostId = appendPostId; - clone.leaveThanksOnStart = leaveThanksOnStart; - clone.connectionTimeout = connectionTimeout; - clone.maxAttempts = maxAttempts; - clone.vProxy = vProxy; - clone.maxEventLog = maxEventLog; - return clone; - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/tasks/AddPostRunnable.java b/vripper-server/src/main/java/tn/mnlr/vripper/tasks/AddPostRunnable.java deleted file mode 100644 index c838ed12..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/tasks/AddPostRunnable.java +++ /dev/null @@ -1,138 +0,0 @@ -package tn.mnlr.vripper.tasks; - -import lombok.extern.slf4j.Slf4j; -import tn.mnlr.vripper.SpringContext; -import tn.mnlr.vripper.Utils; -import tn.mnlr.vripper.download.DownloadService; -import tn.mnlr.vripper.exception.PostParseException; -import tn.mnlr.vripper.jpa.domain.Image; -import tn.mnlr.vripper.jpa.domain.LogEvent; -import tn.mnlr.vripper.jpa.domain.Post; -import tn.mnlr.vripper.jpa.domain.enums.Status; -import tn.mnlr.vripper.jpa.repositories.ILogEventRepository; -import tn.mnlr.vripper.services.DataService; -import tn.mnlr.vripper.services.MetadataService; -import tn.mnlr.vripper.services.SettingsService; -import tn.mnlr.vripper.services.VGAuthService; -import tn.mnlr.vripper.services.domain.PostScanParser; -import tn.mnlr.vripper.services.domain.PostScanResult; - -import java.time.LocalDateTime; -import java.util.Map; -import java.util.Set; - -import static tn.mnlr.vripper.jpa.domain.LogEvent.Status.ERROR; -import static tn.mnlr.vripper.jpa.domain.LogEvent.Status.PROCESSING; - -@Slf4j -public class AddPostRunnable implements Runnable { - - private final String postId; - private final String threadId; - private final DataService dataService; - private final MetadataService metadataService; - private final SettingsService settingsService; - private final VGAuthService VGAuthService; - private final LogEvent logEvent; - private final ILogEventRepository eventRepository; - private final String link; - private final DownloadService downloadService; - - public AddPostRunnable(String postId, String threadId) { - this.postId = postId; - this.threadId = threadId; - this.dataService = SpringContext.getBean(DataService.class); - this.metadataService = SpringContext.getBean(MetadataService.class); - this.settingsService = SpringContext.getBean(SettingsService.class); - this.downloadService = SpringContext.getBean(DownloadService.class); - this.VGAuthService = SpringContext.getBean(VGAuthService.class); - this.eventRepository = SpringContext.getBean(ILogEventRepository.class); - link = - settingsService.getSettings().getVProxy() - + String.format("/threads/%s?%s", threadId, (postId != null ? "p=" + postId : "")); - logEvent = - new LogEvent( - LogEvent.Type.POST, - LogEvent.Status.PENDING, - LocalDateTime.now(), - String.format("Processing %s", link)); - eventRepository.save(logEvent); - } - - @Override - public void run() { - - try { - logEvent.setStatus(PROCESSING); - eventRepository.update(logEvent); - if (dataService.exists(postId)) { - log.warn(String.format("skipping %s, already loaded", postId)); - logEvent.setMessage(String.format("Gallery %s is already loaded", link)); - logEvent.setStatus(ERROR); - eventRepository.update(logEvent); - return; - } - - PostScanParser postScanParser = new PostScanParser(threadId, postId); - - PostScanResult postScanResult; - try { - postScanResult = postScanParser.parse(); - } catch (PostParseException e) { - String error = String.format("parsing failed for gallery %s", link); - log.error(error, e); - logEvent.setMessage(error + "\n" + Utils.throwableToString(e)); - logEvent.setStatus(ERROR); - eventRepository.update(logEvent); - return; - } - if (postScanResult.getPost().isEmpty()) { - String error = String.format("Gallery %s contains no galleries", link); - log.error(error); - logEvent.setMessage(error); - logEvent.setStatus(ERROR); - eventRepository.update(logEvent); - return; - } - if (postScanResult.getImages().isEmpty()) { - String error = String.format("Gallery %s contains no images to download", link); - log.error(error); - logEvent.setMessage(error); - logEvent.setStatus(ERROR); - eventRepository.update(logEvent); - return; - } - - Post post = postScanResult.getPost().get(); - Set images = postScanResult.getImages(); - - dataService.newPost(post, images); - metadataService.startFetchingMetadata(post); - - if (settingsService.getSettings().getAutoStart()) { - log.debug("Auto start downloads option is enabled"); - post.setStatus(Status.PENDING); - downloadService.enqueue(Map.of(post, images)); - log.debug(String.format("Done enqueuing jobs for %s", post.getUrl())); - } else { - post.setStatus(Status.STOPPED); - log.debug("Auto start downloads option is disabled"); - } - if (settingsService.getSettings().getLeaveThanksOnStart() != null - && !settingsService.getSettings().getLeaveThanksOnStart()) { - VGAuthService.leaveThanks(post); - } - dataService.updatePostStatus(post.getStatus(), post.getId()); - logEvent.setMessage( - String.format("Gallery %s is successfully added to download queue", link)); - logEvent.setStatus(LogEvent.Status.DONE); - eventRepository.update(logEvent); - } catch (Exception e) { - String error = String.format("Error when adding gallery %s", link); - log.error(error, e); - logEvent.setMessage(error + "\n" + Utils.throwableToString(e)); - logEvent.setStatus(ERROR); - eventRepository.update(logEvent); - } - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/tasks/AddQueuedRunnable.java b/vripper-server/src/main/java/tn/mnlr/vripper/tasks/AddQueuedRunnable.java deleted file mode 100644 index daff6e5a..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/tasks/AddQueuedRunnable.java +++ /dev/null @@ -1,103 +0,0 @@ -package tn.mnlr.vripper.tasks; - -import lombok.extern.slf4j.Slf4j; -import tn.mnlr.vripper.SpringContext; -import tn.mnlr.vripper.Utils; -import tn.mnlr.vripper.jpa.domain.LogEvent; -import tn.mnlr.vripper.jpa.domain.Queued; -import tn.mnlr.vripper.jpa.repositories.ILogEventRepository; -import tn.mnlr.vripper.services.DataService; -import tn.mnlr.vripper.services.PostService; -import tn.mnlr.vripper.services.ThreadPoolService; -import tn.mnlr.vripper.services.domain.MultiPostScanResult; - -import java.time.LocalDateTime; - -import static tn.mnlr.vripper.jpa.domain.LogEvent.Status.ERROR; -import static tn.mnlr.vripper.jpa.domain.LogEvent.Status.PROCESSING; - -@Slf4j -public class AddQueuedRunnable implements Runnable { - - private final Queued queued; - private final ThreadPoolService threadPoolService; - private final DataService dataService; - private final ILogEventRepository eventRepository; - private final PostService postService; - private final LogEvent logEvent; - - public AddQueuedRunnable(Queued queued) { - this.queued = queued; - threadPoolService = SpringContext.getBean(ThreadPoolService.class); - dataService = SpringContext.getBean(DataService.class); - eventRepository = SpringContext.getBean(ILogEventRepository.class); - postService = SpringContext.getBean(PostService.class); - logEvent = - new LogEvent( - LogEvent.Type.QUEUED, - LogEvent.Status.PENDING, - LocalDateTime.now(), - String.format("Processing multi-post link %s", queued.getLink())); - eventRepository.save(logEvent); - } - - @Override - public void run() { - try { - logEvent.setStatus(PROCESSING); - eventRepository.update(logEvent); - MultiPostScanResult multiPostScanResult = postService.get(queued); - if (multiPostScanResult == null) { - String message = String.format("Fetching multi-post link %s failed", queued.getLink()); - logEvent.setStatus(ERROR); - logEvent.setMessage(message); - eventRepository.update(logEvent); - return; - } else if (multiPostScanResult.getError() != null) { - String message = - "Nothing found for " + queued.getLink() + "\n" + multiPostScanResult.getError(); - logEvent.setStatus(ERROR); - logEvent.setMessage(message); - eventRepository.update(logEvent); - return; - } else if (multiPostScanResult.getPosts().isEmpty()) { - String message = "Nothing found for " + queued.getLink(); - logEvent.setStatus(ERROR); - logEvent.setMessage(message); - eventRepository.update(logEvent); - return; - } - queued.setTotal(multiPostScanResult.getPosts().size()); - queued.done(); - if (multiPostScanResult.getPosts().size() == 1) { - threadPoolService - .getGeneralExecutor() - .submit( - new AddPostRunnable( - multiPostScanResult.getPosts().get(0).getPostId(), - multiPostScanResult.getPosts().get(0).getThreadId())); - logEvent.setStatus(LogEvent.Status.DONE); - logEvent.setMessage(String.format("Link %s is added to download queue", queued.getLink())); - } else { - if (dataService.findQueuedByThreadId(queued.getThreadId()).isEmpty()) { - dataService.newQueueLink(queued); - logEvent.setStatus(LogEvent.Status.DONE); - logEvent.setMessage( - String.format("Link %s is added to multi-post links", queued.getLink())); - } else { - log.info(String.format("Link %s is already loaded", queued.getLink())); - logEvent.setStatus(LogEvent.Status.ERROR); - logEvent.setMessage( - String.format("%s has already been added to multi-post links", queued.getLink())); - } - } - eventRepository.update(logEvent); - } catch (Exception e) { - String error = String.format("Error when adding multi-post link %s", queued.getLink()); - log.error(error, e); - logEvent.setMessage(error + "\n" + Utils.throwableToString(e)); - logEvent.setStatus(ERROR); - eventRepository.update(logEvent); - } - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/tasks/LeaveThanksRunnable.java b/vripper-server/src/main/java/tn/mnlr/vripper/tasks/LeaveThanksRunnable.java deleted file mode 100644 index 9e8515da..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/tasks/LeaveThanksRunnable.java +++ /dev/null @@ -1,134 +0,0 @@ -package tn.mnlr.vripper.tasks; - -import lombok.extern.slf4j.Slf4j; -import org.apache.http.NameValuePair; -import org.apache.http.client.entity.UrlEncodedFormEntity; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpPost; -import org.apache.http.client.protocol.HttpClientContext; -import org.apache.http.impl.client.CloseableHttpClient; -import org.apache.http.message.BasicNameValuePair; -import org.apache.http.util.EntityUtils; -import tn.mnlr.vripper.SpringContext; -import tn.mnlr.vripper.Utils; -import tn.mnlr.vripper.jpa.domain.LogEvent; -import tn.mnlr.vripper.jpa.domain.Post; -import tn.mnlr.vripper.jpa.repositories.ILogEventRepository; -import tn.mnlr.vripper.services.ConnectionService; -import tn.mnlr.vripper.services.DataService; -import tn.mnlr.vripper.services.SettingsService; - -import java.io.UnsupportedEncodingException; -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.List; - -@Slf4j -public class LeaveThanksRunnable implements Runnable { - - private final ConnectionService cm; - private final DataService dataService; - private final HttpClientContext context; - private final SettingsService settingsService; - private final ILogEventRepository eventRepository; - private final boolean authenticated; - private final Post post; - private final LogEvent logEvent; - - public LeaveThanksRunnable(Post post, boolean authenticated, HttpClientContext context) { - this.post = post; - this.authenticated = authenticated; - this.context = context; - cm = SpringContext.getBean(ConnectionService.class); - dataService = SpringContext.getBean(DataService.class); - eventRepository = SpringContext.getBean(ILogEventRepository.class); - settingsService = SpringContext.getBean(SettingsService.class); - logEvent = - new LogEvent( - LogEvent.Type.THANKS, - LogEvent.Status.PENDING, - LocalDateTime.now(), - String.format("Leaving thanks for %s", post.getUrl())); - eventRepository.save(logEvent); - } - - @Override - public void run() { - try { - logEvent.setStatus(LogEvent.Status.PROCESSING); - eventRepository.update(logEvent); - if (!settingsService.getSettings().getVLogin()) { - logEvent.setMessage( - String.format( - "Will not send a like for %s\nAuthentication with ViperGirls option is disabled", - post.getUrl())); - logEvent.setStatus(LogEvent.Status.DONE); - eventRepository.update(logEvent); - return; - } - if (!settingsService.getSettings().getVThanks()) { - logEvent.setMessage( - String.format( - "Will not send a like for %s\nLeave thanks option is disabled", post.getUrl())); - logEvent.setStatus(LogEvent.Status.DONE); - eventRepository.update(logEvent); - return; - } - if (!authenticated) { - logEvent.setMessage( - String.format("Will not send a like for %s\nYou are not authenticated", post.getUrl())); - logEvent.setStatus(LogEvent.Status.ERROR); - eventRepository.update(logEvent); - return; - } - if (post.isThanked()) { - logEvent.setMessage( - String.format("Will not send a like for %s\nAlready left a thanks", post.getUrl())); - logEvent.setStatus(LogEvent.Status.DONE); - eventRepository.update(logEvent); - return; - } - - HttpPost postThanks = - cm.buildHttpPost(settingsService.getSettings().getVProxy() + "/post_thanks.php", null); - List params = new ArrayList<>(); - params.add(new BasicNameValuePair("do", "post_thanks_add")); - params.add(new BasicNameValuePair("using_ajax", "1")); - params.add(new BasicNameValuePair("p", post.getPostId())); - params.add(new BasicNameValuePair("securitytoken", post.getSecurityToken())); - try { - postThanks.setEntity(new UrlEncodedFormEntity(params)); - } catch (UnsupportedEncodingException e) { - String error = String.format("Request error for %s", post.getUrl()); - log.error(error, e); - logEvent.setMessage(error + "\n" + Utils.throwableToString(e)); - logEvent.setStatus(LogEvent.Status.ERROR); - eventRepository.update(logEvent); - return; - } - - postThanks.addHeader("Referer", settingsService.getSettings().getVProxy()); - postThanks.addHeader( - "Host", - settingsService.getSettings().getVProxy().replace("https://", "").replace("http://", "")); - - CloseableHttpClient client = cm.getClient().build(); - - try (CloseableHttpResponse response = client.execute(postThanks, context)) { - if (response.getStatusLine().getStatusCode() / 100 == 2) { - post.setThanked(true); - dataService.updatePostThanks(post.isThanked(), post.getId()); - } - EntityUtils.consumeQuietly(response.getEntity()); - } - logEvent.setStatus(LogEvent.Status.DONE); - eventRepository.update(logEvent); - } catch (Exception e) { - String error = String.format("Failed to leave a thanks for %s", post.getUrl()); - log.error(error, e); - logEvent.setMessage(error + "\n" + Utils.throwableToString(e)); - logEvent.setStatus(LogEvent.Status.ERROR); - eventRepository.update(logEvent); - } - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/tasks/LinkScanRunnable.java b/vripper-server/src/main/java/tn/mnlr/vripper/tasks/LinkScanRunnable.java deleted file mode 100644 index 8b14f31f..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/tasks/LinkScanRunnable.java +++ /dev/null @@ -1,109 +0,0 @@ -package tn.mnlr.vripper.tasks; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.lang.NonNull; -import tn.mnlr.vripper.SpringContext; -import tn.mnlr.vripper.Utils; -import tn.mnlr.vripper.jpa.domain.LogEvent; -import tn.mnlr.vripper.jpa.domain.Queued; -import tn.mnlr.vripper.jpa.repositories.impl.LogEventRepository; -import tn.mnlr.vripper.services.PostService; -import tn.mnlr.vripper.services.SettingsService; - -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import static tn.mnlr.vripper.jpa.domain.LogEvent.Status.ERROR; - -@Slf4j -public class LinkScanRunnable implements Runnable { - - private static final Object LOCK = new Object(); - private final List urlList; - private final SettingsService settingsService; - private final PostService postService; - private final LogEventRepository eventRepository; - private final LogEvent logEvent; - - public LinkScanRunnable(@NonNull List urlList) { - this.urlList = urlList; - settingsService = SpringContext.getBean(SettingsService.class); - postService = SpringContext.getBean(PostService.class); - eventRepository = SpringContext.getBean(LogEventRepository.class); - logEvent = - new LogEvent( - LogEvent.Type.SCAN, - LogEvent.Status.PENDING, - LocalDateTime.now(), - "Links to scan:\n\t" + String.join("\n\t", urlList)); - eventRepository.save(logEvent); - } - - @Override - public void run() { - synchronized (LOCK) { - try { - logEvent.setStatus(LogEvent.Status.PROCESSING); - eventRepository.update(logEvent); - ArrayList queuedList = new ArrayList<>(); - List unsupported = new ArrayList<>(); - List unrecognized = new ArrayList<>(); - for (String url : urlList) { - log.debug(String.format("Starting to process thread: %s", url)); - if (!url.startsWith(settingsService.getSettings().getVProxy())) { - log.error(String.format("Unsupported link %s", url)); - unsupported.add(url); - continue; - } - - String threadId, postId; - Matcher m = - Pattern.compile( - Pattern.quote(settingsService.getSettings().getVProxy()) - + "/threads/(\\d+)((.*p=)(\\d+))?") - .matcher(url); - if (m.find()) { - threadId = m.group(1); - postId = m.group(4); - } else { - log.error(String.format("Cannot retrieve thread id from URL %s", url)); - unrecognized.add(url); - continue; - } - queuedList.add(new Queued(url, threadId, postId)); - } - StringBuilder errorMessage = new StringBuilder(); - if (!unsupported.isEmpty()) { - errorMessage - .append("Unsupported links:\n\t") - .append(String.join("\n\t", unsupported)) - .append("\n\n"); - } - if (!unrecognized.isEmpty()) { - errorMessage - .append("Unrecognized links:\n\t") - .append(String.join("\n\t", unrecognized)) - .append("\n\n"); - } - - postService.processMultiPost(queuedList); - if (!unsupported.isEmpty() || !unrecognized.isEmpty()) { - logEvent.setStatus(LogEvent.Status.ERROR); - logEvent.setMessage("Some links failed to be scanned: \n" + errorMessage); - } else { - logEvent.setStatus(LogEvent.Status.DONE); - } - eventRepository.update(logEvent); - } catch (Exception e) { - String error = "Error when scanning links"; - log.error(error, e); - logEvent.setMessage(error + "\n" + Utils.throwableToString(e)); - logEvent.setStatus(ERROR); - eventRepository.update(logEvent); - } - } - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/tasks/MetadataRunnable.java b/vripper-server/src/main/java/tn/mnlr/vripper/tasks/MetadataRunnable.java deleted file mode 100644 index 7067128b..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/tasks/MetadataRunnable.java +++ /dev/null @@ -1,246 +0,0 @@ -package tn.mnlr.vripper.tasks; - -import lombok.Getter; -import lombok.NonNull; -import lombok.extern.slf4j.Slf4j; -import net.jodah.failsafe.Failsafe; -import org.apache.http.client.HttpClient; -import org.apache.http.client.methods.AbstractExecutionAwareRequest; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.apache.http.client.protocol.HttpClientContext; -import org.apache.http.util.EntityUtils; -import org.w3c.dom.Document; -import org.w3c.dom.Node; -import tn.mnlr.vripper.SpringContext; -import tn.mnlr.vripper.Utils; -import tn.mnlr.vripper.download.DownloadJob; -import tn.mnlr.vripper.exception.DownloadException; -import tn.mnlr.vripper.exception.PostParseException; -import tn.mnlr.vripper.jpa.domain.LogEvent; -import tn.mnlr.vripper.jpa.domain.Metadata; -import tn.mnlr.vripper.jpa.domain.Post; -import tn.mnlr.vripper.jpa.repositories.ILogEventRepository; -import tn.mnlr.vripper.services.*; - -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; -import java.util.stream.Collectors; - -import static tn.mnlr.vripper.jpa.domain.LogEvent.Status.ERROR; - -@Slf4j -public class MetadataRunnable implements Runnable { - - private static final List dictionary = - Arrays.asList("download", "link", "rapidgator", "filefactory", "filefox"); - - @Getter private final Post post; - - private final DataService dataService; - private final ILogEventRepository eventRepository; - - private final LogEvent logEvent; - private final ConnectionService cm; - private final HtmlProcessorService htmlProcessorService; - private final XpathService xpathService; - - private final HttpClientContext context = HttpClientContext.create(); - private final MetadataService metadataService; - private volatile boolean stopped = false; - @Getter private volatile boolean finished = false; - - public MetadataRunnable(@NonNull Post post) { - this.post = post; - dataService = SpringContext.getBean(DataService.class); - eventRepository = SpringContext.getBean(ILogEventRepository.class); - cm = SpringContext.getBean(ConnectionService.class); - VGAuthService vgAuthService = SpringContext.getBean(VGAuthService.class); - htmlProcessorService = SpringContext.getBean(HtmlProcessorService.class); - xpathService = SpringContext.getBean(XpathService.class); - metadataService = SpringContext.getBean(MetadataService.class); - - context.setCookieStore(vgAuthService.getContext().getCookieStore()); - context.setAttribute( - DownloadJob.ContextAttributes.OPEN_CONNECTION.toString(), - Collections.synchronizedList(new ArrayList())); - - logEvent = - new LogEvent( - LogEvent.Type.METADATA, - LogEvent.Status.PENDING, - LocalDateTime.now(), - "Fetching metadata for " + post.getUrl()); - eventRepository.save(logEvent); - } - - @Override - public void run() { - - try { - logEvent.setStatus(LogEvent.Status.PROCESSING); - eventRepository.update(logEvent); - Metadata metadata = fetchMetadata(post.getPostId(), post.getThreadId(), post.getUrl()); - if (metadata != null && !stopped) { - dataService.setMetadata(post, metadata); - logEvent.setStatus(LogEvent.Status.DONE); - } else { - logEvent.setStatus(ERROR); - logEvent.setMessage(String.format("Fetching metadata for %s failed", post.getUrl())); - } - eventRepository.update(logEvent); - } catch (Exception e) { - String message = String.format("Failed to fetch metadata for %s", post.getUrl()); - log.error(message, e); - logEvent.setMessage(message + "\n" + Utils.throwableToString(e)); - logEvent.setStatus(ERROR); - eventRepository.update(logEvent); - } finally { - if (stopped) { - String message = String.format("Fetching metadata for %s interrupted", post.getUrl()); - logEvent.setStatus(LogEvent.Status.DONE); - logEvent.setMessage(message); - eventRepository.update(logEvent); - } - finished = true; - metadataService.stopFetchingMetadata(List.of(post.getPostId())); - } - } - - private Metadata fetchMetadata(String postId, String threadId, String url) { - HttpGet httpGet = cm.buildHttpGet(url, context); - AtomicReference metadataReference = new AtomicReference<>(); - Failsafe.with(cm.getRetryPolicy()) - .onFailure( - e -> - log.error( - String.format("Error occurred when getting post metadata, postId %s", postId), - e.getFailure())) - .run( - () -> { - if (stopped) { - return; - } - HttpClient connection = cm.getClient().build(); - try (CloseableHttpResponse response = - (CloseableHttpResponse) connection.execute(httpGet, context)) { - if (stopped) { - return; - } - if (response.getStatusLine().getStatusCode() / 100 != 2) { - throw new DownloadException( - String.format( - "Unexpected response code '%d' for %s", - response.getStatusLine().getStatusCode(), httpGet)); - } - try { - Document document = - htmlProcessorService.clean(EntityUtils.toString(response.getEntity())); - - if (stopped) { - return; - } - - Node postNode = - xpathService.getAsNode( - document, - String.format( - "//li[@id='post_%s']/div[contains(@class, 'postdetails')]", postId)); - - if (stopped) { - return; - } - - String postedBy = - xpathService - .getAsNode( - postNode, - "./div[contains(@class, 'userinfo')]//a[contains(@class, 'username')]//font") - .getTextContent() - .trim(); - - if (stopped) { - return; - } - - Metadata metadata = new Metadata(); - metadata.setPostedBy(postedBy); - - if (stopped) { - return; - } - - Node node = - xpathService.getAsNode( - document, String.format("//div[@id='post_message_%s']", postId)); - metadata.setResolvedNames(findTitleInContent(node)); - metadata.setPostId(postId); - - if (stopped) { - return; - } - metadataReference.set(metadata); - } catch (Exception e) { - if (stopped) { - log.warn(e.getMessage(), e); - return; - } - throw new PostParseException( - String.format("Failed to parse thread %s, post %s", threadId, postId), e); - } finally { - EntityUtils.consumeQuietly(response.getEntity()); - } - } - }); - return metadataReference.get(); - } - - private List findTitleInContent(Node node) { - List altTitle = new ArrayList<>(); - findTitle(node, altTitle, new AtomicBoolean(true)); - return altTitle.stream().distinct().collect(Collectors.toList()); - } - - private void findTitle(Node node, List altTitle, AtomicBoolean keepGoing) { - if (!keepGoing.get()) { - return; - } - if (node.getNodeName().equals("a") || node.getNodeName().equals("img")) { - keepGoing.set(false); - return; - } - if (node.getNodeType() == Node.ELEMENT_NODE) { - for (int i = 0; i < node.getChildNodes().getLength(); i++) { - Node item = node.getChildNodes().item(i); - findTitle(item, altTitle, keepGoing); - if (!keepGoing.get()) { - return; - } - } - - } else if (node.getNodeType() == Node.TEXT_NODE) { - String text = node.getTextContent().trim(); - if (!text.isBlank() - && dictionary.stream().noneMatch(e -> text.toLowerCase().contains(e.toLowerCase()))) { - altTitle.add(text); - } - } - } - - public void stop() { - this.stopped = true; - List requests = - (List) - this.context.getAttribute(DownloadJob.ContextAttributes.OPEN_CONNECTION.toString()); - if (requests != null) { - for (AbstractExecutionAwareRequest request : requests) { - request.abort(); - } - } - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/web/restendpoints/EventLogRestEndpoint.java b/vripper-server/src/main/java/tn/mnlr/vripper/web/restendpoints/EventLogRestEndpoint.java deleted file mode 100644 index 41531758..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/web/restendpoints/EventLogRestEndpoint.java +++ /dev/null @@ -1,27 +0,0 @@ -package tn.mnlr.vripper.web.restendpoints; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.CrossOrigin; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.ResponseStatus; -import org.springframework.web.bind.annotation.RestController; -import tn.mnlr.vripper.jpa.repositories.ILogEventRepository; - -@Slf4j -@RestController -@CrossOrigin(value = "*") -public class EventLogRestEndpoint { - - private final ILogEventRepository eventRepository; - - public EventLogRestEndpoint(ILogEventRepository eventRepository) { - this.eventRepository = eventRepository; - } - - @GetMapping("/events/clear") - @ResponseStatus(value = HttpStatus.OK) - public void clear() { - eventRepository.deleteAll(); - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/web/restendpoints/PostRestEndpoint.java b/vripper-server/src/main/java/tn/mnlr/vripper/web/restendpoints/PostRestEndpoint.java deleted file mode 100644 index ad14ea29..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/web/restendpoints/PostRestEndpoint.java +++ /dev/null @@ -1,268 +0,0 @@ -package tn.mnlr.vripper.web.restendpoints; - -import lombok.NonNull; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.*; -import tn.mnlr.vripper.download.DownloadService; -import tn.mnlr.vripper.jpa.domain.Metadata; -import tn.mnlr.vripper.jpa.domain.Post; -import tn.mnlr.vripper.jpa.domain.Queued; -import tn.mnlr.vripper.services.*; -import tn.mnlr.vripper.services.domain.MultiPostItem; -import tn.mnlr.vripper.services.domain.MultiPostScanResult; -import tn.mnlr.vripper.tasks.AddPostRunnable; -import tn.mnlr.vripper.tasks.LinkScanRunnable; -import tn.mnlr.vripper.web.restendpoints.domain.*; -import tn.mnlr.vripper.web.restendpoints.exceptions.BadRequestException; -import tn.mnlr.vripper.web.restendpoints.exceptions.NotFoundException; -import tn.mnlr.vripper.web.restendpoints.exceptions.ServerErrorException; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; - -@Slf4j -@RestController -@CrossOrigin(value = "*") -public class PostRestEndpoint { - - private static final Object LOCK = new Object(); - private final DataService dataService; - private final PathService pathService; - private final DownloadService downloadService; - private final PostService postService; - private final ThreadPoolService threadPoolService; - private final SettingsService settingsService; - - @Autowired - public PostRestEndpoint( - DataService dataService, - PathService pathService, - DownloadService downloadService, - PostService postService, - ThreadPoolService threadPoolService, - SettingsService settingsService) { - this.dataService = dataService; - this.pathService = pathService; - this.downloadService = downloadService; - this.postService = postService; - this.threadPoolService = threadPoolService; - this.settingsService = settingsService; - } - - @PostMapping("/post") - @ResponseStatus(code = HttpStatus.OK) - public void processPost(@RequestBody ThreadUrl _url) { - synchronized (LOCK) { - if (_url.getUrl() == null || _url.getUrl().isBlank()) { - log.error("Cannot process empty requests"); - throw new BadRequestException("Cannot process empty requests"); - } - List urlList = - Arrays.stream(_url.getUrl().split("\\r?\\n")) - .map(String::trim) - .filter(e -> !e.isEmpty()) - .collect(Collectors.toList()); - threadPoolService.getGeneralExecutor().submit(new LinkScanRunnable(urlList)); - } - } - - @PostMapping("/post/restart") - @ResponseStatus(value = HttpStatus.OK) - public void restartPost(@RequestBody @NonNull List postIds) { - synchronized (LOCK) { - downloadService.restartAll( - postIds.stream().map(PostId::getPostId).collect(Collectors.toList())); - } - } - - @PostMapping("/post/add") - @ResponseStatus(value = HttpStatus.OK) - public void addPost(@RequestBody List posts) { - synchronized (LOCK) { - for (PostToAdd post : posts) { - threadPoolService - .getGeneralExecutor() - .submit(new AddPostRunnable(post.getPostId(), post.getThreadId())); - } - } - } - - @GetMapping("/post/path/{postId}") - @ResponseStatus(value = HttpStatus.OK) - public DownloadPath folderPath(@PathVariable("postId") String postId) { - synchronized (LOCK) { - return getDownloadPath(postId); - } - } - - private DownloadPath getDownloadPath(String postId) { - Optional _post = dataService.findPostByPostId(postId); - if (_post.isPresent()) { - Post post = _post.get(); - if (post.getDownloadDirectory() == null) { - log.error("Download has not been started yet for this post"); - throw new NotFoundException("Download has not been started yet for this post"); - } else { - return new DownloadPath( - pathService.calcDownloadDirectory(post, settingsService.getSettings()).getPath()); - } - } else { - log.error(String.format("Unable to find post with postId = %s", postId)); - throw new NotFoundException(String.format("Unable to find post with postId = %s", postId)); - } - } - - @PostMapping("/post/restart/all") - @ResponseStatus(value = HttpStatus.OK) - public void restartPost() { - synchronized (LOCK) { - downloadService.restartAll(null); - } - } - - @PostMapping("/post/stop") - @ResponseStatus(value = HttpStatus.OK) - public void stop(@RequestBody @NonNull List postIds) { - synchronized (LOCK) { - downloadService.stopAll(postIds.stream().map(PostId::getPostId).collect(Collectors.toList())); - } - } - - @PostMapping("/post/stop/all") - @ResponseStatus(value = HttpStatus.OK) - public void stopAll() { - synchronized (LOCK) { - downloadService.stopAll(null); - } - } - - @PostMapping("/post/remove") - @ResponseStatus(value = HttpStatus.OK) - public List remove(@RequestBody @NonNull List postIds) { - synchronized (LOCK) { - List result = new ArrayList<>(); - List collect = - postIds.stream() - .map(PostId::getPostId) - .peek(e -> result.add(new RemoveResult(e))) - .collect(Collectors.toList()); - downloadService.stopAll(collect); - dataService.removeAll(collect); - return result; - } - } - - @PostMapping("/post/rename") - @ResponseStatus(value = HttpStatus.OK) - public List rename(@RequestBody @NonNull List postToRename) { - synchronized (LOCK) { - renamePosts(postToRename); - return postToRename; - } - } - - private void renamePosts(@RequestBody @NonNull List postToRename) { - synchronized (LOCK) { - for (AltPostName altPostName : postToRename) { - try { - pathService.rename( - altPostName.getPostId(), altPostName.getAltName(), settingsService.getSettings()); - } catch (Exception e) { - log.error( - String.format("Failed to rename post with postId = %s", altPostName.getPostId()), e); - throw new ServerErrorException(e.getMessage()); - } - } - } - } - - @PostMapping("/post/rename/first") - @ResponseStatus(value = HttpStatus.OK) - public List renameFirst(@RequestBody @NonNull List postToRename) { - synchronized (LOCK) { - renamePostsToFirst(postToRename); - return postToRename; - } - } - - public void renamePostsToFirst(@RequestBody @NonNull List postToRename) { - synchronized (LOCK) { - for (PostId postId : postToRename) { - Optional _metadata = dataService.findMetadataByPostId(postId.getPostId()); - if (_metadata.isPresent()) { - Metadata metadata = _metadata.get(); - if (metadata.getResolvedNames() != null) { - List resolvedNames = metadata.getResolvedNames(); - if (!resolvedNames.isEmpty()) { - String altTitle = resolvedNames.get(0); - try { - pathService.rename(postId.getPostId(), altTitle, settingsService.getSettings()); - } catch (Exception e) { - log.error( - String.format("Failed to rename post with postId = %s", postId.getPostId()), e); - throw new ServerErrorException(e.getMessage()); - } - } - } - } - } - } - } - - @PostMapping("/post/clear/all") - @ResponseStatus(value = HttpStatus.OK) - public RemoveAllResult clearAll() { - synchronized (LOCK) { - return new RemoveAllResult(dataService.clearCompleted()); - } - } - - @GetMapping("/grab/{threadId}") - @ResponseStatus(value = HttpStatus.OK) - public List grab(@PathVariable("threadId") @NonNull String threadId) { - synchronized (LOCK) { - try { - Queued queued = - dataService - .findQueuedByThreadId(threadId) - .orElseThrow( - () -> - new NotFoundException( - String.format("Unable to find links for threadId = %s", threadId))); - MultiPostScanResult multiPostScanResult = postService.get(queued); - if (multiPostScanResult == null) { - log.error(String.format("Failed to get links for threadId = %s", threadId)); - throw new ServerErrorException( - String.format("Failed to get links for threadId = %s", threadId)); - } else { - return multiPostScanResult.getPosts(); - } - } catch (Exception e) { - throw new ServerErrorException( - String.format("Failed to get links for threadId = %s, %s", threadId, e.getMessage())); - } - } - } - - @PostMapping("/grab/remove") - @ResponseStatus(value = HttpStatus.OK) - public ThreadId grabRemove(@RequestBody @NonNull ThreadId threadId) { - synchronized (LOCK) { - postService.remove(threadId.getThreadId()); - return threadId; - } - } - - @GetMapping("/grab/clear") - @ResponseStatus(value = HttpStatus.OK) - public void grabClear() { - synchronized (LOCK) { - dataService.clearQueueLinks(); - } - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/web/restendpoints/SettingsRestEndpoint.java b/vripper-server/src/main/java/tn/mnlr/vripper/web/restendpoints/SettingsRestEndpoint.java deleted file mode 100644 index a99887c0..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/web/restendpoints/SettingsRestEndpoint.java +++ /dev/null @@ -1,66 +0,0 @@ -package tn.mnlr.vripper.web.restendpoints; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.*; -import tn.mnlr.vripper.exception.ValidationException; -import tn.mnlr.vripper.services.SettingsService; -import tn.mnlr.vripper.services.domain.Settings; -import tn.mnlr.vripper.web.restendpoints.exceptions.BadRequestException; - -import java.util.List; - -@RestController -@Slf4j -@CrossOrigin(value = "*") -public class SettingsRestEndpoint { - - private final SettingsService settingsService; - - @Autowired - public SettingsRestEndpoint(SettingsService settingsService) { - this.settingsService = settingsService; - } - - @PostMapping("/settings/theme") - @ResponseStatus(value = HttpStatus.OK) - public SettingsService.Theme postTheme(@RequestBody SettingsService.Theme theme) { - this.settingsService.setTheme(theme); - return settingsService.getTheme(); - } - - @GetMapping("/settings/theme") - @ResponseStatus(value = HttpStatus.OK) - public SettingsService.Theme getTheme() { - return settingsService.getTheme(); - } - - @PostMapping("/settings") - @ResponseStatus(value = HttpStatus.OK) - public Settings postSettings(@RequestBody Settings settings) { - - try { - this.settingsService.check(settings); - } catch (ValidationException e) { - log.error("Invalid settings", e); - throw new BadRequestException(e.getMessage()); - } - - this.settingsService.newSettings(settings); - return getAppSettingsService(); - } - - @GetMapping("/settings") - @ResponseStatus(value = HttpStatus.OK) - public Settings getAppSettingsService() { - - return settingsService.getSettings(); - } - - @GetMapping("/settings/proxies") - @ResponseStatus(value = HttpStatus.OK) - public List mirrors() { - return settingsService.getProxies(); - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/web/restendpoints/WebSecurityConfig.java b/vripper-server/src/main/java/tn/mnlr/vripper/web/restendpoints/WebSecurityConfig.java deleted file mode 100644 index f1ce2318..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/web/restendpoints/WebSecurityConfig.java +++ /dev/null @@ -1,14 +0,0 @@ -package tn.mnlr.vripper.web.restendpoints; - -import org.springframework.context.annotation.Configuration; -import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; - -@Configuration -public class WebSecurityConfig implements WebMvcConfigurer { - - @Override - public void addViewControllers(ViewControllerRegistry registry) { - registry.addViewController("/home").setViewName("forward:/"); - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/web/restendpoints/domain/AltPostName.java b/vripper-server/src/main/java/tn/mnlr/vripper/web/restendpoints/domain/AltPostName.java deleted file mode 100644 index 241eaf2d..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/web/restendpoints/domain/AltPostName.java +++ /dev/null @@ -1,15 +0,0 @@ -package tn.mnlr.vripper.web.restendpoints.domain; - -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.ToString; - -@Getter -@Setter -@NoArgsConstructor -@ToString -public class AltPostName { - private String postId; - private String altName; -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/web/restendpoints/domain/DownloadPath.java b/vripper-server/src/main/java/tn/mnlr/vripper/web/restendpoints/domain/DownloadPath.java deleted file mode 100644 index 5f6eb055..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/web/restendpoints/domain/DownloadPath.java +++ /dev/null @@ -1,18 +0,0 @@ -package tn.mnlr.vripper.web.restendpoints.domain; - -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.ToString; - -@Getter -@Setter -@NoArgsConstructor -@ToString -public class DownloadPath { - private String path; - - public DownloadPath(String path) { - this.path = path; - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/web/restendpoints/domain/PostId.java b/vripper-server/src/main/java/tn/mnlr/vripper/web/restendpoints/domain/PostId.java deleted file mode 100644 index 5079a38b..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/web/restendpoints/domain/PostId.java +++ /dev/null @@ -1,18 +0,0 @@ -package tn.mnlr.vripper.web.restendpoints.domain; - -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.ToString; - -@Getter -@Setter -@NoArgsConstructor -@ToString -public class PostId { - private String postId; - - public PostId(String postId) { - this.postId = postId; - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/web/restendpoints/domain/PostToAdd.java b/vripper-server/src/main/java/tn/mnlr/vripper/web/restendpoints/domain/PostToAdd.java deleted file mode 100644 index aedd9c82..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/web/restendpoints/domain/PostToAdd.java +++ /dev/null @@ -1,15 +0,0 @@ -package tn.mnlr.vripper.web.restendpoints.domain; - -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.ToString; - -@Getter -@Setter -@NoArgsConstructor -@ToString -public class PostToAdd { - private String threadId; - private String postId; -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/web/restendpoints/domain/RemoveAllResult.java b/vripper-server/src/main/java/tn/mnlr/vripper/web/restendpoints/domain/RemoveAllResult.java deleted file mode 100644 index 1dd91456..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/web/restendpoints/domain/RemoveAllResult.java +++ /dev/null @@ -1,22 +0,0 @@ -package tn.mnlr.vripper.web.restendpoints.domain; - -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.ToString; - -import java.util.List; - -@Getter -@Setter -@NoArgsConstructor -@ToString -public class RemoveAllResult { - private List postIds; - private int removed; - - public RemoveAllResult(List postIds) { - this.removed = postIds.size(); - this.postIds = postIds; - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/web/restendpoints/domain/RemoveResult.java b/vripper-server/src/main/java/tn/mnlr/vripper/web/restendpoints/domain/RemoveResult.java deleted file mode 100644 index 66351dda..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/web/restendpoints/domain/RemoveResult.java +++ /dev/null @@ -1,18 +0,0 @@ -package tn.mnlr.vripper.web.restendpoints.domain; - -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.ToString; - -@Getter -@Setter -@NoArgsConstructor -@ToString -public class RemoveResult { - private String postId; - - public RemoveResult(String postId) { - this.postId = postId; - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/web/restendpoints/domain/ThreadId.java b/vripper-server/src/main/java/tn/mnlr/vripper/web/restendpoints/domain/ThreadId.java deleted file mode 100644 index a02d08df..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/web/restendpoints/domain/ThreadId.java +++ /dev/null @@ -1,14 +0,0 @@ -package tn.mnlr.vripper.web.restendpoints.domain; - -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.ToString; - -@Getter -@Setter -@NoArgsConstructor -@ToString -public class ThreadId { - private String threadId; -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/web/restendpoints/domain/ThreadUrl.java b/vripper-server/src/main/java/tn/mnlr/vripper/web/restendpoints/domain/ThreadUrl.java deleted file mode 100644 index 275a1ca0..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/web/restendpoints/domain/ThreadUrl.java +++ /dev/null @@ -1,14 +0,0 @@ -package tn.mnlr.vripper.web.restendpoints.domain; - -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import lombok.ToString; - -@Getter -@Setter -@NoArgsConstructor -@ToString -public class ThreadUrl { - private String url; -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/web/wsendpoints/DataBroadcast.java b/vripper-server/src/main/java/tn/mnlr/vripper/web/wsendpoints/DataBroadcast.java deleted file mode 100644 index fd157eef..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/web/wsendpoints/DataBroadcast.java +++ /dev/null @@ -1,158 +0,0 @@ -package tn.mnlr.vripper.web.wsendpoints; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.messaging.simp.SimpMessagingTemplate; -import org.springframework.stereotype.Service; -import reactor.core.Disposable; -import tn.mnlr.vripper.event.Event; -import tn.mnlr.vripper.event.EventBus; -import tn.mnlr.vripper.jpa.domain.Image; -import tn.mnlr.vripper.services.DataService; -import tn.mnlr.vripper.services.domain.DownloadSpeed; -import tn.mnlr.vripper.services.domain.GlobalState; - -import javax.annotation.PostConstruct; -import javax.annotation.PreDestroy; -import java.time.Duration; -import java.time.temporal.ChronoUnit; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.stream.Collectors; - -@Service -@Slf4j -public class DataBroadcast { - - private final SimpMessagingTemplate template; - private final DataService dataService; - private final EventBus eventBus; - - private Disposable disposable; - - @Autowired - public DataBroadcast(SimpMessagingTemplate template, DataService dataService, EventBus eventBus) { - this.template = template; - this.dataService = dataService; - this.eventBus = eventBus; - } - - @PostConstruct - private void run() { - - disposable = - eventBus - .flux() - .buffer(Duration.of(500, ChronoUnit.MILLIS)) - .subscribe( - data -> { - Map>> eventMap = - data.stream().collect(Collectors.groupingBy(Event::getKind)); - - eventMap.forEach( - (kind, eventList) -> { - switch (kind) { - case POST_UPDATE: - case METADATA_UPDATE: - template.convertAndSend( - "/topic/posts", - eventList.stream() - .map(e -> ((Long) e.getData())) - .distinct() - .map(dataService::findById) - .filter(Optional::isPresent) - .map(Optional::get) - .collect(Collectors.toUnmodifiableSet())); - - break; - case POST_REMOVE: - template.convertAndSend( - "/topic/posts/deleted", - eventList.stream() - .map(e -> ((String) e.getData())) - .collect(Collectors.toUnmodifiableSet())); - break; - case IMAGE_UPDATE: - eventList.stream() - .map(e -> ((Long) e.getData())) - .distinct() - .map(dataService::findImageById) - .filter(Optional::isPresent) - .map(Optional::get) - .collect(Collectors.groupingBy(Image::getPostId)) - .forEach( - (postId, images) -> - template.convertAndSend("/topic/images/" + postId, images)); - break; - - case QUEUED_UPDATE: - template.convertAndSend( - "/topic/queued", - eventList.stream() - .map(e -> ((Long) e.getData())) - .distinct() - .map(dataService::findQueuedById) - .filter(Optional::isPresent) - .map(Optional::get) - .collect(Collectors.toUnmodifiableSet())); - break; - case QUEUED_REMOVE: - template.convertAndSend( - "/topic/queued/deleted", - eventList.stream() - .map(e -> ((String) e.getData())) - .collect(Collectors.toUnmodifiableSet())); - break; - case LOG_EVENT_UPDATE: - template.convertAndSend( - "/topic/events", - eventList.stream() - .map(e -> ((Long) e.getData())) - .distinct() - .map(dataService::findEventById) - .filter(Optional::isPresent) - .map(Optional::get) - .collect(Collectors.toUnmodifiableSet())); - break; - case LOG_EVENT_REMOVE: - template.convertAndSend( - "/topic/events/deleted", - eventList.stream() - .map(e -> ((Long) e.getData())) - .collect(Collectors.toUnmodifiableSet())); - break; - case VG_USER: - eventList.stream() - .map(e -> new DataController.LoggedUser((String) e.getData())) - .distinct() - .forEach(user -> template.convertAndSend("/topic/user", user)); - break; - case GLOBAL_STATE: - eventList.stream() - .map(e -> ((GlobalState) e.getData())) - .distinct() - .forEach( - globalState -> - template.convertAndSend( - "/topic/download-state", globalState)); - break; - case BYTES_PER_SECOND: - eventList.stream() - .map(e -> new DownloadSpeed((Long) e.getData())) - .distinct() - .forEach(speed -> template.convertAndSend("/topic/speed", speed)); - - break; - } - }); - }); - } - - @PreDestroy - private void destroy() { - if (disposable != null) { - disposable.dispose(); - } - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/web/wsendpoints/DataController.java b/vripper-server/src/main/java/tn/mnlr/vripper/web/wsendpoints/DataController.java deleted file mode 100644 index 922f2303..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/web/wsendpoints/DataController.java +++ /dev/null @@ -1,89 +0,0 @@ -package tn.mnlr.vripper.web.wsendpoints; - -import lombok.Getter; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.messaging.handler.annotation.DestinationVariable; -import org.springframework.messaging.simp.annotation.SubscribeMapping; -import org.springframework.stereotype.Controller; -import tn.mnlr.vripper.jpa.domain.Image; -import tn.mnlr.vripper.jpa.domain.LogEvent; -import tn.mnlr.vripper.jpa.domain.Post; -import tn.mnlr.vripper.jpa.domain.Queued; -import tn.mnlr.vripper.services.DataService; -import tn.mnlr.vripper.services.DownloadSpeedService; -import tn.mnlr.vripper.services.GlobalStateService; -import tn.mnlr.vripper.services.VGAuthService; -import tn.mnlr.vripper.services.domain.DownloadSpeed; -import tn.mnlr.vripper.services.domain.GlobalState; - -import java.util.Collection; -import java.util.Comparator; -import java.util.List; - -@Controller -public class DataController { - - private final VGAuthService VGAuthService; - private final GlobalStateService globalStateService; - private final DownloadSpeedService downloadSpeedService; - private final DataService dataService; - - @Autowired - public DataController( - VGAuthService VGAuthService, - GlobalStateService globalStateService, - DownloadSpeedService downloadSpeedService, - DataService dataService) { - this.VGAuthService = VGAuthService; - this.globalStateService = globalStateService; - this.downloadSpeedService = downloadSpeedService; - this.dataService = dataService; - } - - @SubscribeMapping("/user") - public LoggedUser user() { - return new LoggedUser(VGAuthService.getLoggedUser()); - } - - @SubscribeMapping("/download-state") - public GlobalState downloadState() { - return globalStateService.getCurrentState(); - } - - @SubscribeMapping("/speed") - public DownloadSpeed speed() { - return new DownloadSpeed(downloadSpeedService.getCurrentValue()); - } - - @SubscribeMapping("/posts") - public Collection posts() { - List posts = dataService.findAllPosts(); - posts.sort(Comparator.comparing(Post::getAddedOn)); - return posts; - } - - @SubscribeMapping("/images/{postId}") - public List postsDetails(@DestinationVariable("postId") String postId) { - return dataService.findImagesByPostId(postId); - } - - @SubscribeMapping("/queued") - public Collection queued() { - return dataService.findAllQueued(); - } - - @SubscribeMapping("/events") - public Collection events() { - return dataService.findAllEvents(); - } - - @Getter - public static class LoggedUser { - - private final String user; - - LoggedUser(String user) { - this.user = user; - } - } -} diff --git a/vripper-server/src/main/java/tn/mnlr/vripper/web/wsendpoints/WebSocketConfig.java b/vripper-server/src/main/java/tn/mnlr/vripper/web/wsendpoints/WebSocketConfig.java deleted file mode 100644 index 4b682c25..00000000 --- a/vripper-server/src/main/java/tn/mnlr/vripper/web/wsendpoints/WebSocketConfig.java +++ /dev/null @@ -1,23 +0,0 @@ -package tn.mnlr.vripper.web.wsendpoints; - -import org.springframework.context.annotation.Configuration; -import org.springframework.messaging.simp.config.MessageBrokerRegistry; -import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker; -import org.springframework.web.socket.config.annotation.StompEndpointRegistry; -import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer; - -@Configuration -@EnableWebSocketMessageBroker -public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { - - @Override - public void registerStompEndpoints(StompEndpointRegistry registry) { - registry.addEndpoint("/ws").setAllowedOrigins("*"); - } - - @Override - public void configureMessageBroker(MessageBrokerRegistry registry) { - registry.setApplicationDestinationPrefixes("/app", "/topic"); - registry.enableSimpleBroker("/topic"); - } -} diff --git a/vripper-server/src/main/resources/db/changelog/db.changelog-master.xml b/vripper-server/src/main/resources/db/changelog/db.changelog-master.xml deleted file mode 100644 index fcb349dc..00000000 --- a/vripper-server/src/main/resources/db/changelog/db.changelog-master.xml +++ /dev/null @@ -1,178 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/vripper-server/src/main/resources/logback-spring.xml b/vripper-server/src/main/resources/logback-spring.xml deleted file mode 100644 index 397a6185..00000000 --- a/vripper-server/src/main/resources/logback-spring.xml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/vripper-ui/e2e/protractor.conf.js b/vripper-ui/e2e/protractor.conf.js deleted file mode 100644 index 86776a39..00000000 --- a/vripper-ui/e2e/protractor.conf.js +++ /dev/null @@ -1,28 +0,0 @@ -// Protractor configuration file, see link for more information -// https://github.com/angular/protractor/blob/master/lib/config.ts - -const { SpecReporter } = require('jasmine-spec-reporter'); - -exports.config = { - allScriptsTimeout: 11000, - specs: [ - './src/**/*.e2e-spec.ts' - ], - capabilities: { - 'browserName': 'chrome' - }, - directConnect: true, - baseUrl: 'http://localhost:4200/', - framework: 'jasmine', - jasmineNodeOpts: { - showColors: true, - defaultTimeoutInterval: 30000, - print: function() {} - }, - onPrepare() { - require('ts-node').register({ - project: require('path').join(__dirname, './tsconfig.e2e.json') - }); - jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); - } -}; \ No newline at end of file diff --git a/vripper-ui/e2e/src/app.e2e-spec.ts b/vripper-ui/e2e/src/app.e2e-spec.ts deleted file mode 100644 index 4f9ab422..00000000 --- a/vripper-ui/e2e/src/app.e2e-spec.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { AppPage } from './app.po'; -import { browser, logging } from 'protractor'; - -describe('workspace-project App', () => { - let page: AppPage; - - beforeEach(() => { - page = new AppPage(); - }); - - it('should display welcome message', () => { - page.navigateTo(); - expect(page.getTitleText()).toEqual('Welcome to vripper-ui!'); - }); - - afterEach(async () => { - // Assert that there are no errors emitted from the browser - const logs = await browser.manage().logs().get(logging.Type.BROWSER); - expect(logs).not.toContain(jasmine.objectContaining({ - level: logging.Level.SEVERE, - })); - }); -}); diff --git a/vripper-ui/e2e/src/app.po.ts b/vripper-ui/e2e/src/app.po.ts deleted file mode 100644 index 72e463a3..00000000 --- a/vripper-ui/e2e/src/app.po.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { browser, by, element } from 'protractor'; - -export class AppPage { - navigateTo() { - return browser.get('/'); - } - - getTitleText() { - return element(by.css('app-root h1')).getText(); - } -} diff --git a/vripper-ui/e2e/tsconfig.e2e.json b/vripper-ui/e2e/tsconfig.e2e.json deleted file mode 100644 index a6dd6220..00000000 --- a/vripper-ui/e2e/tsconfig.e2e.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "outDir": "../out-tsc/app", - "module": "commonjs", - "target": "es5", - "types": [ - "jasmine", - "jasminewd2", - "node" - ] - } -} \ No newline at end of file diff --git a/vripper-ui/package.json b/vripper-ui/package.json deleted file mode 100644 index 86258173..00000000 --- a/vripper-ui/package.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "name": "vripper-ui", - "version": "3.5.4", - "scripts": { - "ng": "ng", - "start": "ng serve", - "build": "ng build", - "build-electron": "ng build --prod --base-href ./", - "build-web": "ng build --prod", - "test": "ng test", - "lint": "ng lint", - "e2e": "ng e2e" - }, - "private": true, - "dependencies": { - "@angular/animations": "11.0.8", - "@angular/cdk": "11.0.3", - "@angular/common": "11.0.8", - "@angular/compiler": "11.0.8", - "@angular/core": "11.0.8", - "@angular/flex-layout": "11.0.0-beta.33", - "@angular/forms": "11.0.8", - "@angular/material": "11.0.3", - "@angular/platform-browser": "11.0.8", - "@angular/platform-browser-dynamic": "11.0.8", - "@angular/router": "11.0.8", - "@mdi/angular-material": "^5.8.55", - "@stomp/rx-stomp": "^0.3.5", - "ag-grid-angular": "24.1.0", - "ag-grid-community": "23.2.1", - "core-js": "3.6.5", - "ngx-electron": "2.2.0", - "rxjs": "6.6.3", - "tslib": "^2.0.1", - "zone.js": "0.10.3" - }, - "devDependencies": { - "@angular-devkit/build-angular": "^0.1100.6", - "@angular/cli": "11.0.6", - "@angular/compiler-cli": "11.0.8", - "@angular/language-service": "11.0.8", - "@types/jasmine": "3.5.12", - "@types/jasminewd2": "2.0.8", - "@types/node": "12.12.21", - "codelyzer": "6.0.0", - "electron": "11.1.1", - "jasmine-core": "~3.6.0", - "jasmine-spec-reporter": "~5.0.0", - "karma": "~5.1.1", - "karma-chrome-launcher": "~3.1.0", - "karma-coverage-istanbul-reporter": "~3.0.2", - "karma-jasmine": "~4.0.1", - "karma-jasmine-html-reporter": "^1.5.0", - "protractor": "~7.0.0", - "ts-node": "8.10.2", - "tslint": "~6.1.3", - "typescript": "4.0.5" - } -} diff --git a/vripper-ui/src/app/about/about-component.component-scss-theme.scss b/vripper-ui/src/app/about/about-component.component-scss-theme.scss deleted file mode 100644 index 34a779ab..00000000 --- a/vripper-ui/src/app/about/about-component.component-scss-theme.scss +++ /dev/null @@ -1,10 +0,0 @@ -@import '~@angular/material/theming'; - -@mixin about-component-theme($theme) { - $foreground: map-get($theme, foreground); - $background: map-get($theme, background); - - .about-link, .about-icon { - color: mat-color($foreground, text); - } -} diff --git a/vripper-ui/src/app/app.component.scss-theme.scss b/vripper-ui/src/app/app.component.scss-theme.scss deleted file mode 100644 index b3f21d6b..00000000 --- a/vripper-ui/src/app/app.component.scss-theme.scss +++ /dev/null @@ -1,11 +0,0 @@ -@import '~@angular/material/theming'; - -@mixin app-component-theme($theme) { - - $background: map-get($theme, background); - - button.add-button { - - background-color: mat-color($background, background); - } -} diff --git a/vripper-ui/src/app/domain/download-speed.model.ts b/vripper-ui/src/app/domain/download-speed.model.ts deleted file mode 100644 index 959f3fc6..00000000 --- a/vripper-ui/src/app/domain/download-speed.model.ts +++ /dev/null @@ -1,4 +0,0 @@ -export class DownloadSpeed { - constructor(public speed: string) { - } -} diff --git a/vripper-ui/src/app/domain/logged-user.model.ts b/vripper-ui/src/app/domain/logged-user.model.ts deleted file mode 100644 index ddf66c1f..00000000 --- a/vripper-ui/src/app/domain/logged-user.model.ts +++ /dev/null @@ -1,3 +0,0 @@ -export interface LoggedUser { - user: string -} diff --git a/vripper-ui/src/app/domain/settings.model.ts b/vripper-ui/src/app/domain/settings.model.ts deleted file mode 100644 index 28dfc9c8..00000000 --- a/vripper-ui/src/app/domain/settings.model.ts +++ /dev/null @@ -1,17 +0,0 @@ -export interface Settings { - downloadPath: string; - maxThreads: number; - maxTotalThreads: number; - autoStart: boolean; - vLogin: boolean; - vUsername: string; - vPassword: string; - vThanks: boolean; - desktopClipboard: boolean; - viewPhotos: boolean; - resolveTitle: boolean; - connectionTimeout: number; - maxAttempts: number; - vProxy: string; - maxEventLog: number; -} diff --git a/vripper-ui/src/app/event-log/event-log.component.html b/vripper-ui/src/app/event-log/event-log.component.html deleted file mode 100644 index fdae3ef5..00000000 --- a/vripper-ui/src/app/event-log/event-log.component.html +++ /dev/null @@ -1,4 +0,0 @@ -
- - -
diff --git a/vripper-ui/src/app/multi-post-grid/multi-post-grid.component.html b/vripper-ui/src/app/multi-post-grid/multi-post-grid.component.html deleted file mode 100644 index fdae3ef5..00000000 --- a/vripper-ui/src/app/multi-post-grid/multi-post-grid.component.html +++ /dev/null @@ -1,4 +0,0 @@ -
- - -
diff --git a/vripper-ui/src/app/posts/alternative-title/alternative-title.component.html b/vripper-ui/src/app/posts/alternative-title/alternative-title.component.html deleted file mode 100644 index 77e8c062..00000000 --- a/vripper-ui/src/app/posts/alternative-title/alternative-title.component.html +++ /dev/null @@ -1,26 +0,0 @@ -
-

Alternative title

- - -
- - Choose a title - - - {{altName}} - - - - - - -
-
- - - - -
- diff --git a/vripper-ui/src/app/posts/alternative-title/alternative-title.component.scss b/vripper-ui/src/app/posts/alternative-title/alternative-title.component.scss deleted file mode 100644 index bbfcb12c..00000000 --- a/vripper-ui/src/app/posts/alternative-title/alternative-title.component.scss +++ /dev/null @@ -1,7 +0,0 @@ -form { - height: 100%; -} - -form > mat-form-field { - width: 100%; -} diff --git a/vripper-ui/src/app/posts/alternative-title/alternative-title.component.ts b/vripper-ui/src/app/posts/alternative-title/alternative-title.component.ts deleted file mode 100644 index 8b1a0f2a..00000000 --- a/vripper-ui/src/app/posts/alternative-title/alternative-title.component.ts +++ /dev/null @@ -1,57 +0,0 @@ -import {ChangeDetectionStrategy, Component, Inject, OnInit} from '@angular/core'; -import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog'; -import {RenamePostModel} from '../../domain/rename-post.model'; -import {HttpClient} from '@angular/common/http'; -import {ServerService} from '../../services/server-service'; -import {MatSnackBar} from '@angular/material/snack-bar'; -import {FormControl, FormGroup, Validators} from '@angular/forms'; -import {Post} from '../../domain/post-state.model'; - -export interface AlternativeTitleDialog { - post: Post -} - -@Component({ - selector: 'app-alternative-title', - templateUrl: './alternative-title.component.html', - styleUrls: ['./alternative-title.component.scss'], - changeDetection: ChangeDetectionStrategy.OnPush -}) -export class AlternativeTitleComponent implements OnInit { - - form = new FormGroup({ - altNameSelect: new FormControl(''), - altNameInput: new FormControl('', Validators.required) - }); - - constructor( - public dialogRef: MatDialogRef, - @Inject(MAT_DIALOG_DATA) public data: AlternativeTitleDialog, - private httpClient: HttpClient, - private serverService: ServerService, - private _snackBar: MatSnackBar) { - } - - ngOnInit(): void { - this.form.get('altNameSelect').valueChanges.subscribe(v => this.form.get('altNameInput').setValue(v)); - if (this.data.post?.metadata?.resolvedNames == null || this.data.post?.metadata?.resolvedNames.length == 0) { - this.form.get('altNameInput').setValue(this.data.post.title); - } - } - - rename() { - this.httpClient.post(this.serverService.baseUrl + '/post/rename', [{ - postId: this.data.post.postId, - altName: this.form.get('altNameInput').value - }]).subscribe( - () => { - }, - error => { - this._snackBar.open(error?.error?.message || 'Unexpected error, check log file', null, { - duration: 5000 - }); - } - ); - } - -} diff --git a/vripper-ui/src/app/posts/posts.component.html b/vripper-ui/src/app/posts/posts.component.html deleted file mode 100644 index fdae3ef5..00000000 --- a/vripper-ui/src/app/posts/posts.component.html +++ /dev/null @@ -1,4 +0,0 @@ -
- - -
diff --git a/vripper-ui/src/app/services/clipboard.service.ts b/vripper-ui/src/app/services/clipboard.service.ts deleted file mode 100644 index 518a938c..00000000 --- a/vripper-ui/src/app/services/clipboard.service.ts +++ /dev/null @@ -1,72 +0,0 @@ -import {Observable, Subject} from 'rxjs'; -import {ElectronService} from 'ngx-electron'; -import {Injectable} from '@angular/core'; -import {HttpClient} from '@angular/common/http'; -import {Settings} from '../domain/settings.model'; -import {ServerService} from './server-service'; -import Clipboard = Electron.Clipboard; - -@Injectable({ - providedIn: 'root' -}) -export class ClipboardService { - private links$: Subject = new Subject(); - private interval; - private lastText = ''; - - constructor( - private electronService: ElectronService, - private serverService: ServerService, - private httpClient: HttpClient - ) { - } - - get links(): Observable { - return this.links$.asObservable(); - } - - init(settings?: Settings) { - if (!settings) { - this.httpClient.get(this.serverService.baseUrl + '/settings').subscribe( - data => { - this._init(data); - }, - error => { - console.error(error); - } - ); - return; - } else { - this._init(settings); - } - } - - _init(settings: Settings) { - if (!this.electronService.isElectronApp) { - console.log('Clipboard deactivated, not an electron app'); - return; - } - if (this.interval != null) { - clearInterval(this.interval); - } - if (!settings.desktopClipboard) { - return; - } - - const clipboard: Clipboard = this.electronService.clipboard; - this.interval = setInterval(() => { - const text = clipboard.readText(); - - if (this.textHasDiff(text, this.lastText)) { - this.lastText = text; - if (text.indexOf('https://vipergirls.to/threads') !== -1) { - this.links$.next(text); - } - } - }, 500); - } - - private textHasDiff(a, b) { - return a && b !== a; - } -} diff --git a/vripper-ui/src/app/status-bar/status-bar.component.scss-theme.scss b/vripper-ui/src/app/status-bar/status-bar.component.scss-theme.scss deleted file mode 100644 index 05ebd4c0..00000000 --- a/vripper-ui/src/app/status-bar/status-bar.component.scss-theme.scss +++ /dev/null @@ -1,11 +0,0 @@ -@import '~@angular/material/theming'; - -@mixin status-bar-component-theme($theme) { - $foreground: map-get($theme, foreground); - $background: map-get($theme, background); - - .status-bar { - background-color: mat-color($background, background); - color: mat-color($foreground, text); - } -} diff --git a/vripper-ui/src/app/toolbar/toolbar.component.scss-theme.scss b/vripper-ui/src/app/toolbar/toolbar.component.scss-theme.scss deleted file mode 100644 index ddbfe328..00000000 --- a/vripper-ui/src/app/toolbar/toolbar.component.scss-theme.scss +++ /dev/null @@ -1,65 +0,0 @@ -@import '~@angular/material/theming'; - -@mixin toolbar-theme($theme, $dark: false) { - $primary: map-get($theme, primary); - $accent: map-get($theme, accent); - $warn: map-get($theme, warn); - $foreground: map-get($theme, foreground); - $background: map-get($theme, background); - - app-toolbar .mat-toolbar { - @if $dark { - background: mat-color($background); - } @else { - background: mat-color($accent); - } - } - - app-toolbar .mat-form-field-label { - @if $dark { - color: mat-color($foreground, text); - } @else { - color: mat-color($background, background); - } - } - - app-toolbar .mat-icon { - @if $dark { - color: mat-color($foreground, text); - } @else { - color: mat-color($background, background); - } - } - - app-toolbar .mat-form-field { - @if $dark { - color: mat-color($foreground, text); - } @else { - color: mat-color($background, background); - } - } - - app-toolbar .logged-user { - @if $dark { - color: mat-color($foreground, text); - } @else { - color: mat-color($background, background); - } - } - - app-toolbar .mat-icon-button.mat-button-disabled .mat-icon { - @if $dark { - color: mat-color($foreground, disabled-text); - } @else { - color: mat-color($foreground, disabled-text); - } - } - - app-toolbar .add-button .mat-icon { - @if $dark { - color: mat-color($foreground); - } @else { - color: mat-color($accent); - } - } -} diff --git a/vripper-ui/src/dark-theme.scss b/vripper-ui/src/dark-theme.scss deleted file mode 100644 index 34fb901c..00000000 --- a/vripper-ui/src/dark-theme.scss +++ /dev/null @@ -1,9 +0,0 @@ -@import '~@angular/material/theming'; - -$my-dark-theme-primary: mat-palette($mat-grey, 600); -$my-dark-theme-accent: mat-palette($mat-grey, 600); - -$my-dark-theme: mat-dark-theme( - $my-dark-theme-primary, - $my-dark-theme-accent -); diff --git a/vripper-ui/src/grid-theme.scss b/vripper-ui/src/grid-theme.scss deleted file mode 100644 index c36f4937..00000000 --- a/vripper-ui/src/grid-theme.scss +++ /dev/null @@ -1,166 +0,0 @@ -@import '~@angular/material/theming'; - -@import "~ag-grid-community/src/styles/ag-grid.scss"; -@import "~ag-grid-community/src/styles/ag-theme-alpine/sass/ag-theme-alpine-mixin.scss"; - -@mixin grid-theme($theme, $dark: false) { - $primary: map-get($theme, primary); - $accent: map-get($theme, accent); - $warn: map-get($theme, warn); - $foreground: map-get($theme, foreground); - $background: map-get($theme, background); - $red: mat-palette($mat-red); - $grey: mat-palette($mat-grey); - $green: mat-palette($mat-green); - $orange: mat-palette($mat-orange); - $blue: mat-palette($mat-blue); - - .ag-theme-alpine { - @include ag-theme-alpine(( - odd-row-background-color: null, - row-border-color: transparent, - row-hover-color: null, - font-family: (Open, sans-serif), - font-size: 13px - )); - - .ag-checkbox-input-wrapper.ag-checked::after { - color: mat-color($accent); - } - - .ag-checkbox { - margin-right: 5px; - } - - .ag-header-row, .ag-sort-order { - color: mat-color($foreground, text); - } - - .ag-header, .ag-header .ag-icon { - color: mat-color($foreground, text); - background-color: mat-color($background, background); - } - - .ag-root-wrapper { - background-color: mat-color($background, background); - - & .ag-header-cell { - background-color: mat-color($background, background); - } - } - - .ag-row { - background-color: mat-color($background, background); - color: mat-color($foreground, text); - - & a { - color: mat-color($foreground, text); - } - } - - .ag-row-selected { - background-color: transparent; - } - - .ag-center-cols-viewport { - overflow: hidden; - } - - .ag-cell-focus, .ag-cell { - border: none !important; - } - - .ag-row-selected { - background-color: mat-color($background, selected-button) !important; - } - - .text-cell { - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - } - - /* Progress bar section */ - - .native-progress-bar-container { - width: 100%; - height: 100%; - display: flex; - justify-content: start; - align-items: center; - } - - .native-progress-bar-back { - height: 4px; - width: 100%; - background-color: lightgrey; - } - - .native-progress-bar-front { - transition: width 0.75s; - height: 100%; - @if $dark { - background-color: #4a4a4a; - } @else { - background-color: mat-color($primary); - } - } - - /* End progress bar section */ - - /* Status section */ - - .native-status-container { - display: flex; - align-items: center; - justify-content: start; - } - - .native-status-icon { - height: 24px; - margin-right: 5px; - } - - .native-status-text { - line-height: 24px; - } - - .native-status-icon.error { - color: mat-color($red, 400); - } - - .native-status-icon.partial { - color: mat-color($orange, 400); - } - - .native-status-icon.downloading { - color: mat-color($blue, 400); - } - - .native-status-icon.complete { - color: mat-color($green, 400); - } - - .native-status-icon.pending { - color: mat-color($grey, 600); - } - - .native-status-icon.stopped { - color: mat-color($grey, 600); - } - - /* End status section */ - - /* Title section */ - - .cell-icon { - @if $dark { - color: mat-color($accent); - } @else { - color: mat-color($primary); - } - } - - /* End title section */ - } -} diff --git a/vripper-ui/src/karma.conf.js b/vripper-ui/src/karma.conf.js deleted file mode 100644 index ee9caa15..00000000 --- a/vripper-ui/src/karma.conf.js +++ /dev/null @@ -1,31 +0,0 @@ -// Karma configuration file, see link for more information -// https://karma-runner.github.io/1.0/config/configuration-file.html - -module.exports = function (config) { - config.set({ - basePath: '', - frameworks: ['jasmine', '@angular-devkit/build-angular'], - plugins: [ - require('karma-jasmine'), - require('karma-chrome-launcher'), - require('karma-jasmine-html-reporter'), - require('karma-coverage-istanbul-reporter'), - require('@angular-devkit/build-angular/plugins/karma') - ], - client: { - clearContext: false // leave Jasmine Spec Runner output visible in browser - }, - coverageIstanbulReporter: { - dir: require('path').join(__dirname, '../coverage'), - reports: ['html', 'lcovonly', 'text-summary'], - fixWebpackSourcePaths: true - }, - reporters: ['progress', 'kjhtml'], - port: 9876, - colors: true, - logLevel: config.LOG_INFO, - autoWatch: true, - browsers: ['Chrome'], - singleRun: false - }); -}; \ No newline at end of file diff --git a/vripper-ui/src/light-theme.scss b/vripper-ui/src/light-theme.scss deleted file mode 100644 index fb59480a..00000000 --- a/vripper-ui/src/light-theme.scss +++ /dev/null @@ -1,9 +0,0 @@ -@import '~@angular/material/theming'; - -$my-light-theme-primary: mat-palette($mat-red, 900); -$my-light-theme-accent: mat-palette($mat-red, 900); - -$my-light-theme: mat-light-theme( - $my-light-theme-primary, - $my-light-theme-accent -); diff --git a/vripper-ui/src/mat-theme.scss b/vripper-ui/src/mat-theme.scss deleted file mode 100644 index b46aa5c8..00000000 --- a/vripper-ui/src/mat-theme.scss +++ /dev/null @@ -1,11 +0,0 @@ -@import '~@angular/material/theming'; - -$custom-typography: mat-typography-config( - $font-family: 'Open, sans-serif' -); - -@include angular-material-typography($custom-typography); - -@include mat-core($custom-typography); -@import 'light-theme.scss'; -@import 'dark-theme.scss'; diff --git a/vripper-ui/src/polyfills.ts b/vripper-ui/src/polyfills.ts deleted file mode 100644 index c64a485b..00000000 --- a/vripper-ui/src/polyfills.ts +++ /dev/null @@ -1,85 +0,0 @@ -/** - * This file includes polyfills needed by Angular and is loaded before the app. - * You can add your own extra polyfills to this file. - * - * This file is divided into 2 sections: - * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. - * 2. Application imports. Files imported after ZoneJS that should be loaded before your main - * file. - * - * The current setup is for so-called "evergreen" browsers; the last versions of browsers that - * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), - * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. - * - * Learn more in https://angular.io/guide/browser-support - */ - -/*************************************************************************************************** - * BROWSER POLYFILLS - */ - -/** IE9, IE10, IE11, and Chrome <55 requires all of the following polyfills. - * This also includes Android Emulators with older versions of Chrome and Google Search/Googlebot - */ - -// import 'core-js/es6/symbol'; -// import 'core-js/es6/object'; -// import 'core-js/es6/function'; -// import 'core-js/es6/parse-int'; -// import 'core-js/es6/parse-float'; -// import 'core-js/es6/number'; -// import 'core-js/es6/math'; -// import 'core-js/es6/string'; -// import 'core-js/es6/date'; -// import 'core-js/es6/array'; -// import 'core-js/es6/regexp'; -// import 'core-js/es6/map'; -// import 'core-js/es6/weak-map'; -// import 'core-js/es6/set'; - -/** IE10 and IE11 requires the following for NgClass support on SVG elements */ -// import 'classlist.js'; // Run `npm install --save classlist.js`. - -/** IE10 and IE11 requires the following for the Reflect API. */ -// import 'core-js/es6/reflect'; - -/** - * Web Animations `@angular/platform-browser/animations` - * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. - * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). - */ -// import 'web-animations-js'; // Run `npm install --save web-animations-js`. - -/** - * By default, zone.js will patch all possible macroTask and DomEvents - * user can disable parts of macroTask/DomEvents patch by setting following flags - * because those flags need to be set before `zone.js` being loaded, and webpack - * will put import in the top of bundle, so user need to create a separate file - * in this directory (for example: zone-flags.ts), and put the following flags - * into that file, and then add the following code before importing zone.js. - * import './zone-flags.ts'; - * - * The flags allowed in zone-flags.ts are listed here. - * - * The following flags will work for all browsers. - * - * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame - * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick - * (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames - * - * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js - * with the following flag, it will bypass `zone.js` patch for IE/Edge - * - * (window as any).__Zone_enable_cross_context_check = true; - * - */ - -/*************************************************************************************************** - * Zone JS is required by default for Angular itself. - */ -import 'zone.js/dist/zone'; // Included with Angular CLI. - - -/*************************************************************************************************** - * APPLICATION IMPORTS - */ diff --git a/vripper-ui/src/test.ts b/vripper-ui/src/test.ts deleted file mode 100644 index ee6a3631..00000000 --- a/vripper-ui/src/test.ts +++ /dev/null @@ -1,17 +0,0 @@ -// This file is required by karma.conf.js and loads recursively all the .spec and framework files - -import 'zone.js/dist/zone-testing'; -import {getTestBed} from '@angular/core/testing'; -import {BrowserDynamicTestingModule, platformBrowserDynamicTesting} from '@angular/platform-browser-dynamic/testing'; - -declare const require: any; - -// First, initialize the Angular testing environment. -getTestBed().initTestEnvironment( - BrowserDynamicTestingModule, - platformBrowserDynamicTesting() -); -// Then we find all the tests. -const context = require.context('./', true, /\.spec\.ts$/); -// And load the modules. -context.keys().map(context); diff --git a/vripper-ui/src/tsconfig.app.json b/vripper-ui/src/tsconfig.app.json deleted file mode 100644 index 6af6e87a..00000000 --- a/vripper-ui/src/tsconfig.app.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "outDir": "../out-tsc/app", - "types": [] - }, - "files": [ - "main.ts", - "polyfills.ts" - ] -} diff --git a/vripper-ui/src/tsconfig.spec.json b/vripper-ui/src/tsconfig.spec.json deleted file mode 100644 index de773363..00000000 --- a/vripper-ui/src/tsconfig.spec.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "extends": "../tsconfig.json", - "compilerOptions": { - "outDir": "../out-tsc/spec", - "types": [ - "jasmine", - "node" - ] - }, - "files": [ - "test.ts", - "polyfills.ts" - ], - "include": [ - "**/*.spec.ts", - "**/*.d.ts" - ] -} diff --git a/vripper-ui/src/tslint.json b/vripper-ui/src/tslint.json deleted file mode 100644 index aa7c3eeb..00000000 --- a/vripper-ui/src/tslint.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "extends": "../tslint.json", - "rules": { - "directive-selector": [ - true, - "attribute", - "app", - "camelCase" - ], - "component-selector": [ - true, - "element", - "app", - "kebab-case" - ] - } -} diff --git a/vripper-ui/tsconfig.json b/vripper-ui/tsconfig.json deleted file mode 100644 index e4f4ac9d..00000000 --- a/vripper-ui/tsconfig.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "compileOnSave": false, - "compilerOptions": { - "baseUrl": "./", - "outDir": "./dist/out-tsc", - "sourceMap": true, - "declaration": false, - "module": "es2020", - "moduleResolution": "node", - "emitDecoratorMetadata": true, - "experimentalDecorators": true, - "importHelpers": true, - "target": "es5", - "typeRoots": [ - "node_modules/@types" - ], - "lib": [ - "es2018", - "dom" - ] - } -} diff --git a/vripper-ui/tslint.json b/vripper-ui/tslint.json deleted file mode 100644 index 43700156..00000000 --- a/vripper-ui/tslint.json +++ /dev/null @@ -1,130 +0,0 @@ -{ - "rulesDirectory": [ - "codelyzer" - ], - "rules": { - "arrow-return-shorthand": true, - "callable-types": true, - "class-name": true, - "comment-format": [ - true, - "check-space" - ], - "curly": true, - "deprecation": { - "severity": "warn" - }, - "eofline": true, - "forin": true, - "import-blacklist": [ - true, - "rxjs/Rx" - ], - "import-spacing": true, - "indent": [ - true, - "spaces" - ], - "interface-over-type-literal": true, - "label-position": true, - "max-line-length": [ - true, - 140 - ], - "member-access": false, - "member-ordering": [ - true, - { - "order": [ - "static-field", - "instance-field", - "static-method", - "instance-method" - ] - } - ], - "no-arg": true, - "no-bitwise": true, - "no-console": [ - true, - "debug", - "info", - "time", - "timeEnd", - "trace" - ], - "no-construct": true, - "no-debugger": true, - "no-duplicate-super": true, - "no-empty": false, - "no-empty-interface": true, - "no-eval": true, - "no-inferrable-types": [ - true, - "ignore-params" - ], - "no-misused-new": true, - "no-non-null-assertion": true, - "no-redundant-jsdoc": true, - "no-shadowed-variable": true, - "no-string-literal": false, - "no-string-throw": true, - "no-switch-case-fall-through": true, - "no-trailing-whitespace": true, - "no-unnecessary-initializer": true, - "no-unused-expression": true, - "no-var-keyword": true, - "object-literal-sort-keys": false, - "one-line": [ - true, - "check-open-brace", - "check-catch", - "check-else", - "check-whitespace" - ], - "prefer-const": true, - "quotemark": [ - true, - "single" - ], - "radix": true, - "semicolon": [ - true, - "always" - ], - "triple-equals": [ - true, - "allow-null-check" - ], - "typedef-whitespace": [ - true, - { - "call-signature": "nospace", - "index-signature": "nospace", - "parameter": "nospace", - "property-declaration": "nospace", - "variable-declaration": "nospace" - } - ], - "unified-signatures": true, - "variable-name": false, - "whitespace": [ - true, - "check-branch", - "check-decl", - "check-operator", - "check-separator", - "check-type" - ], - "no-output-on-prefix": true, - "use-input-property-decorator": true, - "use-output-property-decorator": true, - "use-host-property-decorator": true, - "no-input-rename": true, - "no-output-rename": true, - "use-life-cycle-interface": true, - "use-pipe-transform-interface": true, - "component-class-suffix": true, - "directive-class-suffix": true - } -} diff --git a/vripper-ui/yarn.lock b/vripper-ui/yarn.lock deleted file mode 100644 index ef3d4d04..00000000 --- a/vripper-ui/yarn.lock +++ /dev/null @@ -1,9500 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@angular-devkit/architect@0.1100.6": - version "0.1100.6" - resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.1100.6.tgz#ce90ffb78d1d945cafc339d4cfc63b3582cb8e6a" - integrity sha512-4O+cg3AimI2bNAxxdu5NrqSf4Oa8r8xL0+G2Ycd3jLoFv0h0ecJiNKEG5F6IpTprb4aexZD6pcxBJCqQ8MmzWQ== - dependencies: - "@angular-devkit/core" "11.0.6" - rxjs "6.6.3" - -"@angular-devkit/architect@0.1100.7": - version "0.1100.7" - resolved "https://registry.yarnpkg.com/@angular-devkit/architect/-/architect-0.1100.7.tgz#6c39b886bbdb5d0e5a2d9921aa5816209041e105" - integrity sha512-b2zv2yiRbdhJ7hJfZsAvGYcqgh2DVtc7gRIPo1eDPvOAKrenmZ4zo/v0PRYScrTsPzqmoCokNA5nIwufwUEnuA== - dependencies: - "@angular-devkit/core" "11.0.7" - rxjs "6.6.3" - -"@angular-devkit/build-angular@^0.1100.6": - version "0.1100.7" - resolved "https://registry.yarnpkg.com/@angular-devkit/build-angular/-/build-angular-0.1100.7.tgz#e5577998e67e89137c2b7c9d232a58b2fdca91dc" - integrity sha512-erc+AtSU46ZIX7A5dmeZ0/G/SQIbqMAGbTKZbf11GePyhT0JAAnfMQtOHMb6AaX85n4yQTg1uMo9f5+8V3lfKA== - dependencies: - "@angular-devkit/architect" "0.1100.7" - "@angular-devkit/build-optimizer" "0.1100.7" - "@angular-devkit/build-webpack" "0.1100.7" - "@angular-devkit/core" "11.0.7" - "@babel/core" "7.12.3" - "@babel/generator" "7.12.1" - "@babel/plugin-transform-runtime" "7.12.1" - "@babel/preset-env" "7.12.1" - "@babel/runtime" "7.12.1" - "@babel/template" "7.10.4" - "@jsdevtools/coverage-istanbul-loader" "3.0.5" - "@ngtools/webpack" "11.0.7" - ansi-colors "4.1.1" - autoprefixer "9.8.6" - babel-loader "8.1.0" - browserslist "^4.9.1" - cacache "15.0.5" - caniuse-lite "^1.0.30001032" - circular-dependency-plugin "5.2.0" - copy-webpack-plugin "6.2.1" - core-js "3.6.5" - css-loader "4.3.0" - cssnano "4.1.10" - file-loader "6.1.1" - find-cache-dir "3.3.1" - glob "7.1.6" - inquirer "7.3.3" - jest-worker "26.5.0" - karma-source-map-support "1.4.0" - less "3.12.2" - less-loader "7.0.2" - license-webpack-plugin "2.3.1" - loader-utils "2.0.0" - mini-css-extract-plugin "1.2.1" - minimatch "3.0.4" - open "7.3.0" - ora "5.1.0" - parse5-html-rewriting-stream "6.0.1" - pnp-webpack-plugin "1.6.4" - postcss "7.0.32" - postcss-import "12.0.1" - postcss-loader "4.0.4" - raw-loader "4.0.2" - regenerator-runtime "0.13.7" - resolve-url-loader "3.1.2" - rimraf "3.0.2" - rollup "2.32.1" - rxjs "6.6.3" - sass "1.27.0" - sass-loader "10.0.5" - semver "7.3.2" - source-map "0.7.3" - source-map-loader "1.1.2" - source-map-support "0.5.19" - speed-measure-webpack-plugin "1.3.3" - style-loader "2.0.0" - stylus "0.54.8" - stylus-loader "4.3.1" - terser "5.3.7" - terser-webpack-plugin "4.2.3" - text-table "0.2.0" - tree-kill "1.2.2" - webpack "4.44.2" - webpack-dev-middleware "3.7.2" - webpack-dev-server "3.11.0" - webpack-merge "5.2.0" - webpack-sources "2.0.1" - webpack-subresource-integrity "1.5.1" - worker-plugin "5.0.0" - -"@angular-devkit/build-optimizer@0.1100.7": - version "0.1100.7" - resolved "https://registry.yarnpkg.com/@angular-devkit/build-optimizer/-/build-optimizer-0.1100.7.tgz#fef39c8db5d178f223d43bd1c6e3a566e19c24c1" - integrity sha512-bHIIub0d1trVAmAX/EaNR6Zo4b7hkscewK394qYYp/w8VKQkLSAPMUbt2YTWN+erR9yyHnJ2y7tBabIui75Wdw== - dependencies: - loader-utils "2.0.0" - source-map "0.7.3" - tslib "2.0.3" - typescript "4.0.5" - webpack-sources "2.0.1" - -"@angular-devkit/build-webpack@0.1100.7": - version "0.1100.7" - resolved "https://registry.yarnpkg.com/@angular-devkit/build-webpack/-/build-webpack-0.1100.7.tgz#d62df6bd70b495df7dc335f80ecd925e0f3b3e4d" - integrity sha512-/6Hudd1hs/GMHX4C/Qk7jueIMNg8NKFJWDEbvMPMgDzTqUIa680PTD6SNSCcY5Cz9mEpdpYCZo5N31JB7dlpOg== - dependencies: - "@angular-devkit/architect" "0.1100.7" - "@angular-devkit/core" "11.0.7" - rxjs "6.6.3" - -"@angular-devkit/core@11.0.6": - version "11.0.6" - resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-11.0.6.tgz#b3ea815ecdea5f77dae58f3f410b268453d7eb8b" - integrity sha512-nhvU5hH01r9qcexAqvIFU233treWWeW3ncs9UFYjD9Hys9sDSvqC3+bvGvl9vCG5FsyY7oDsjaVAipyUc+SFAg== - dependencies: - ajv "6.12.6" - fast-json-stable-stringify "2.1.0" - magic-string "0.25.7" - rxjs "6.6.3" - source-map "0.7.3" - -"@angular-devkit/core@11.0.7": - version "11.0.7" - resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-11.0.7.tgz#05b82be7e11ddc2a7836c777297ee45eee20ff55" - integrity sha512-1GKnIT++YSUHpzzRx9QC0+8yOw4wy+ZpiJVDlroPSeK4FGrTCJqJKenkfRjVFRFOSrzTiJds+IU6kI4+bFbw9g== - dependencies: - ajv "6.12.6" - fast-json-stable-stringify "2.1.0" - magic-string "0.25.7" - rxjs "6.6.3" - source-map "0.7.3" - -"@angular-devkit/schematics@11.0.6": - version "11.0.6" - resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-11.0.6.tgz#06631190cb22609462597cd6659080fc3313582a" - integrity sha512-hCyu/SSSiC6dKl/NxdWctknIrBqKR6pRe7DMArWowrZX6P9oi36LpKEFnKutE8+tXjsOqQj8XMBq9L64sXZWqg== - dependencies: - "@angular-devkit/core" "11.0.6" - ora "5.1.0" - rxjs "6.6.3" - -"@angular/animations@11.0.8": - version "11.0.8" - resolved "https://registry.yarnpkg.com/@angular/animations/-/animations-11.0.8.tgz#abb3a016bc9164176d1edfcbd2783cc3ba6f9ca3" - integrity sha512-LlKR5bO7WsykZGcPDnL7c4q2/eq2fqywD+KMw5FemGLI8mN1mnq30br9/8TgaFwY0Do1OTOUh1JSrn1ybQgTwA== - dependencies: - tslib "^2.0.0" - -"@angular/cdk@11.0.3": - version "11.0.3" - resolved "https://registry.yarnpkg.com/@angular/cdk/-/cdk-11.0.3.tgz#f5a119e28b4ba21a6f013dfd4de41b16083bd25a" - integrity sha512-hgbJXvZURKBnZawwxUrsZE/3a+HCJh2UhoLIng3cn5Q+WIW/4a37knDl8B9DYKBWrCqeINXNcUHVSKkWc/gjCA== - dependencies: - tslib "^2.0.0" - optionalDependencies: - parse5 "^5.0.0" - -"@angular/cli@11.0.6": - version "11.0.6" - resolved "https://registry.yarnpkg.com/@angular/cli/-/cli-11.0.6.tgz#8d65d3ad3841aabe23ff38a41fa6c4f38dd12f66" - integrity sha512-bwrXXyU23HjUlFl0CNCU+XMGa/enooqpMLcTAA15StVpKFHyaA4c57il/aqu+1IuB+zR6rGDzhAABuvRcHd+mQ== - dependencies: - "@angular-devkit/architect" "0.1100.6" - "@angular-devkit/core" "11.0.6" - "@angular-devkit/schematics" "11.0.6" - "@schematics/angular" "11.0.6" - "@schematics/update" "0.1100.6" - "@yarnpkg/lockfile" "1.1.0" - ansi-colors "4.1.1" - debug "4.2.0" - ini "1.3.6" - inquirer "7.3.3" - npm-package-arg "8.1.0" - npm-pick-manifest "6.1.0" - open "7.3.0" - pacote "9.5.12" - resolve "1.18.1" - rimraf "3.0.2" - semver "7.3.2" - symbol-observable "2.0.3" - universal-analytics "0.4.23" - uuid "8.3.1" - -"@angular/common@11.0.8": - version "11.0.8" - resolved "https://registry.yarnpkg.com/@angular/common/-/common-11.0.8.tgz#f3fd177af584a4e59b2a9bc1a90fa45dcb3dd672" - integrity sha512-Or7uSetk29wcKOsDVyJl/2JUC34e/gDNI3HNlpFh98miT8G4tqFKSmuLGRPPanIKqyQQQmquV93VNPOMA+rdYA== - dependencies: - tslib "^2.0.0" - -"@angular/compiler-cli@11.0.8": - version "11.0.8" - resolved "https://registry.yarnpkg.com/@angular/compiler-cli/-/compiler-cli-11.0.8.tgz#4284fc6e9cc7bee5314738cd32ce8bbdba20237c" - integrity sha512-xblOhSgshNzAxNmLdBjdLHMc3Zoc12w5Bz70nrqtAfK9DY6kQx6aAxpbhOr/F5Jl5XFNbvIviOOkG9aqM1Iuuw== - dependencies: - "@babel/core" "^7.8.6" - "@babel/types" "^7.8.6" - canonical-path "1.0.0" - chokidar "^3.0.0" - convert-source-map "^1.5.1" - dependency-graph "^0.7.2" - fs-extra "4.0.2" - magic-string "^0.25.0" - minimist "^1.2.0" - reflect-metadata "^0.1.2" - semver "^6.3.0" - source-map "^0.6.1" - sourcemap-codec "^1.4.8" - tslib "^2.0.0" - yargs "^16.1.1" - -"@angular/compiler@11.0.8": - version "11.0.8" - resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-11.0.8.tgz#da59389d805034a261b839d703bca3134b6157c4" - integrity sha512-v7NjxLvyJl2cxH7roVTMLnaFCLA9gStgndxJcsXtD+hI7hCOFvrzxwpm3J822KYEkwWhB1PMthJwrZ6OWtkp/A== - dependencies: - tslib "^2.0.0" - -"@angular/compiler@9.0.0": - version "9.0.0" - resolved "https://registry.yarnpkg.com/@angular/compiler/-/compiler-9.0.0.tgz#87e0bef4c369b6cadae07e3a4295778fc93799d5" - integrity sha512-ctjwuntPfZZT2mNj2NDIVu51t9cvbhl/16epc5xEwyzyDt76pX9UgwvY+MbXrf/C/FWwdtmNtfP698BKI+9leQ== - -"@angular/core@11.0.8": - version "11.0.8" - resolved "https://registry.yarnpkg.com/@angular/core/-/core-11.0.8.tgz#cc4ffc5506b393bf851fd7b94760d2a66efeb47c" - integrity sha512-NRKCgOtPFKcJpjjSV0NKuoNaD6ZQ3RqXBm9JURbnjVLaTwtCJlQ9H3N/e+HZNw8Ha2TDFJoiYX+RY071cyTMHQ== - dependencies: - tslib "^2.0.0" - -"@angular/core@9.0.0": - version "9.0.0" - resolved "https://registry.yarnpkg.com/@angular/core/-/core-9.0.0.tgz#227dc53e1ac81824f998c6e76000b7efc522641e" - integrity sha512-6Pxgsrf0qF9iFFqmIcWmjJGkkCaCm6V5QNnxMy2KloO3SDq6QuMVRbN9RtC8Urmo25LP+eZ6ZgYqFYpdD8Hd9w== - -"@angular/flex-layout@11.0.0-beta.33": - version "11.0.0-beta.33" - resolved "https://registry.yarnpkg.com/@angular/flex-layout/-/flex-layout-11.0.0-beta.33.tgz#c2d08f90164701b66a62b8c4365b4e22c1e95789" - integrity sha512-unfhw3abZuKtdwQicRStHCYGbANPTHYg4WNRQk/RC5Mxq+4WOp4Q8HI7GqRHCGUYDCGUP7w1sU/oDt8f09nM8w== - dependencies: - tslib "^2.0.0" - -"@angular/forms@11.0.8": - version "11.0.8" - resolved "https://registry.yarnpkg.com/@angular/forms/-/forms-11.0.8.tgz#f0d68407b50174e4c8b6d3b134fafed792db5320" - integrity sha512-soTg9Zc3G08J/+LRq5FxFvVsKoDQb4F29nEVpbH7oK2hof9Qqois9lH8bJy5dHbNh1qNXYmlWjUd7CQ9p+bbUg== - dependencies: - tslib "^2.0.0" - -"@angular/language-service@11.0.8": - version "11.0.8" - resolved "https://registry.yarnpkg.com/@angular/language-service/-/language-service-11.0.8.tgz#2354589115c7f851c016c7d3a68952f37040b80c" - integrity sha512-5iKvCzb0YWCrEvCUCGsmwQxfUQjnAOdeaNHieLmfkpAutojtIrK59rNmyQqA2RBpo2OZMANz0/aOde1N2rHI0w== - -"@angular/material@11.0.3": - version "11.0.3" - resolved "https://registry.yarnpkg.com/@angular/material/-/material-11.0.3.tgz#dd848f1804b07c9654b095d5b1accb13083e91e5" - integrity sha512-YTHmtKGwjAEFAOOmuivcwnINMPHxvM7iZQpTgZqDKNiqiv53cwry2Ctb54suRNT+D794z59D0/r+YocGkzFv3w== - dependencies: - tslib "^2.0.0" - -"@angular/platform-browser-dynamic@11.0.8": - version "11.0.8" - resolved "https://registry.yarnpkg.com/@angular/platform-browser-dynamic/-/platform-browser-dynamic-11.0.8.tgz#f7714f40e0cb3044f7dfd362371d39c9c4501b19" - integrity sha512-rirFL6R0R9wXTYfzZzsF7Zchq48439QQHAQ3Pi0nTDtyn3seHIEWMW1FcwjuPIt/ea5F+kRB4L8fxGK8G1/taA== - dependencies: - tslib "^2.0.0" - -"@angular/platform-browser@11.0.8": - version "11.0.8" - resolved "https://registry.yarnpkg.com/@angular/platform-browser/-/platform-browser-11.0.8.tgz#aa3863512ad63373d9c6d13ecb64cc1357de959e" - integrity sha512-07nbExdtf2fRJh3XEicbCj1fK0/tWmIW/TaWJ9hv1xeQLjPDcplTF0MaPSlow36qyO+q83rsd3hxyFfBTqgOyw== - dependencies: - tslib "^2.0.0" - -"@angular/router@11.0.8": - version "11.0.8" - resolved "https://registry.yarnpkg.com/@angular/router/-/router-11.0.8.tgz#771b4a235eb696d4411e867f13701532b0d95c67" - integrity sha512-vinpCct3r+6wFtGdYNa/l5PCpJqQSB/D9Z+zZBdmDUy9s32dggHG4msmVrYbFH/j7cKDAycqHGVFyj0hKGDQiQ== - dependencies: - tslib "^2.0.0" - -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.13.tgz#dcfc826beef65e75c50e21d3837d7d95798dd658" - integrity sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g== - dependencies: - "@babel/highlight" "^7.12.13" - -"@babel/compat-data@^7.12.1", "@babel/compat-data@^7.13.8": - version "7.13.8" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.13.8.tgz#5b783b9808f15cef71547f1b691f34f8ff6003a6" - integrity sha512-EaI33z19T4qN3xLXsGf48M2cDqa6ei9tPZlfLdb2HC+e/cFtREiRd8hdSqDbwdLB0/+gLwqJmCYASH0z2bUdog== - -"@babel/core@7.12.3": - version "7.12.3" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.12.3.tgz#1b436884e1e3bff6fb1328dc02b208759de92ad8" - integrity sha512-0qXcZYKZp3/6N2jKYVxZv0aNCsxTSVCiK72DTiTYZAu7sjg73W0/aynWjMbiGd87EQL4WyA8reiJVh92AVla9g== - dependencies: - "@babel/code-frame" "^7.10.4" - "@babel/generator" "^7.12.1" - "@babel/helper-module-transforms" "^7.12.1" - "@babel/helpers" "^7.12.1" - "@babel/parser" "^7.12.3" - "@babel/template" "^7.10.4" - "@babel/traverse" "^7.12.1" - "@babel/types" "^7.12.1" - convert-source-map "^1.7.0" - debug "^4.1.0" - gensync "^1.0.0-beta.1" - json5 "^2.1.2" - lodash "^4.17.19" - resolve "^1.3.2" - semver "^5.4.1" - source-map "^0.5.0" - -"@babel/core@^7.7.5", "@babel/core@^7.8.6": - version "7.13.10" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.13.10.tgz#07de050bbd8193fcd8a3c27918c0890613a94559" - integrity sha512-bfIYcT0BdKeAZrovpMqX2Mx5NrgAckGbwT982AkdS5GNfn3KMGiprlBAtmBcFZRUmpaufS6WZFP8trvx8ptFDw== - dependencies: - "@babel/code-frame" "^7.12.13" - "@babel/generator" "^7.13.9" - "@babel/helper-compilation-targets" "^7.13.10" - "@babel/helper-module-transforms" "^7.13.0" - "@babel/helpers" "^7.13.10" - "@babel/parser" "^7.13.10" - "@babel/template" "^7.12.13" - "@babel/traverse" "^7.13.0" - "@babel/types" "^7.13.0" - convert-source-map "^1.7.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.1.2" - lodash "^4.17.19" - semver "^6.3.0" - source-map "^0.5.0" - -"@babel/generator@7.12.1": - version "7.12.1" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.12.1.tgz#0d70be32bdaa03d7c51c8597dda76e0df1f15468" - integrity sha512-DB+6rafIdc9o72Yc3/Ph5h+6hUjeOp66pF0naQBgUFFuPqzQwIlPTm3xZR7YNvduIMtkDIj2t21LSQwnbCrXvg== - dependencies: - "@babel/types" "^7.12.1" - jsesc "^2.5.1" - source-map "^0.5.0" - -"@babel/generator@^7.12.1", "@babel/generator@^7.13.0", "@babel/generator@^7.13.9": - version "7.13.9" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.13.9.tgz#3a7aa96f9efb8e2be42d38d80e2ceb4c64d8de39" - integrity sha512-mHOOmY0Axl/JCTkxTU6Lf5sWOg/v8nUa+Xkt4zMTftX0wqmb6Sh7J8gvcehBw7q0AhrhAR+FDacKjCZ2X8K+Sw== - dependencies: - "@babel/types" "^7.13.0" - jsesc "^2.5.1" - source-map "^0.5.0" - -"@babel/helper-annotate-as-pure@^7.12.13": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.12.13.tgz#0f58e86dfc4bb3b1fcd7db806570e177d439b6ab" - integrity sha512-7YXfX5wQ5aYM/BOlbSccHDbuXXFPxeoUmfWtz8le2yTkTZc+BxsiEnENFoi2SlmA8ewDkG2LgIMIVzzn2h8kfw== - dependencies: - "@babel/types" "^7.12.13" - -"@babel/helper-builder-binary-assignment-operator-visitor@^7.12.13": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.12.13.tgz#6bc20361c88b0a74d05137a65cac8d3cbf6f61fc" - integrity sha512-CZOv9tGphhDRlVjVkAgm8Nhklm9RzSmWpX2my+t7Ua/KT616pEzXsQCjinzvkRvHWJ9itO4f296efroX23XCMA== - dependencies: - "@babel/helper-explode-assignable-expression" "^7.12.13" - "@babel/types" "^7.12.13" - -"@babel/helper-compilation-targets@^7.12.1", "@babel/helper-compilation-targets@^7.13.10", "@babel/helper-compilation-targets@^7.13.8": - version "7.13.10" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.13.10.tgz#1310a1678cb8427c07a753750da4f8ce442bdd0c" - integrity sha512-/Xju7Qg1GQO4mHZ/Kcs6Au7gfafgZnwm+a7sy/ow/tV1sHeraRUHbjdat8/UvDor4Tez+siGKDk6zIKtCPKVJA== - dependencies: - "@babel/compat-data" "^7.13.8" - "@babel/helper-validator-option" "^7.12.17" - browserslist "^4.14.5" - semver "^6.3.0" - -"@babel/helper-create-class-features-plugin@^7.13.0": - version "7.13.10" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.13.10.tgz#073b2bbb925a097643c6fc5770e5f13394e887c9" - integrity sha512-YV7r2YxdTUaw84EwNkyrRke/TJHR/UXGiyvACRqvdVJ2/syV2rQuJNnaRLSuYiop8cMRXOgseTGoJCWX0q2fFg== - dependencies: - "@babel/helper-function-name" "^7.12.13" - "@babel/helper-member-expression-to-functions" "^7.13.0" - "@babel/helper-optimise-call-expression" "^7.12.13" - "@babel/helper-replace-supers" "^7.13.0" - "@babel/helper-split-export-declaration" "^7.12.13" - -"@babel/helper-create-regexp-features-plugin@^7.12.13": - version "7.12.17" - resolved "https://registry.yarnpkg.com/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.12.17.tgz#a2ac87e9e319269ac655b8d4415e94d38d663cb7" - integrity sha512-p2VGmBu9oefLZ2nQpgnEnG0ZlRPvL8gAGvPUMQwUdaE8k49rOMuZpOwdQoy5qJf6K8jL3bcAMhVUlHAjIgJHUg== - dependencies: - "@babel/helper-annotate-as-pure" "^7.12.13" - regexpu-core "^4.7.1" - -"@babel/helper-explode-assignable-expression@^7.12.13": - version "7.13.0" - resolved "https://registry.yarnpkg.com/@babel/helper-explode-assignable-expression/-/helper-explode-assignable-expression-7.13.0.tgz#17b5c59ff473d9f956f40ef570cf3a76ca12657f" - integrity sha512-qS0peLTDP8kOisG1blKbaoBg/o9OSa1qoumMjTK5pM+KDTtpxpsiubnCGP34vK8BXGcb2M9eigwgvoJryrzwWA== - dependencies: - "@babel/types" "^7.13.0" - -"@babel/helper-function-name@^7.12.13": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.12.13.tgz#93ad656db3c3c2232559fd7b2c3dbdcbe0eb377a" - integrity sha512-TZvmPn0UOqmvi5G4vvw0qZTpVptGkB1GL61R6lKvrSdIxGm5Pky7Q3fpKiIkQCAtRCBUwB0PaThlx9vebCDSwA== - dependencies: - "@babel/helper-get-function-arity" "^7.12.13" - "@babel/template" "^7.12.13" - "@babel/types" "^7.12.13" - -"@babel/helper-get-function-arity@^7.12.13": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz#bc63451d403a3b3082b97e1d8b3fe5bd4091e583" - integrity sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg== - dependencies: - "@babel/types" "^7.12.13" - -"@babel/helper-hoist-variables@^7.13.0": - version "7.13.0" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.13.0.tgz#5d5882e855b5c5eda91e0cadc26c6e7a2c8593d8" - integrity sha512-0kBzvXiIKfsCA0y6cFEIJf4OdzfpRuNk4+YTeHZpGGc666SATFKTz6sRncwFnQk7/ugJ4dSrCj6iJuvW4Qwr2g== - dependencies: - "@babel/traverse" "^7.13.0" - "@babel/types" "^7.13.0" - -"@babel/helper-member-expression-to-functions@^7.13.0": - version "7.13.0" - resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.13.0.tgz#6aa4bb678e0f8c22f58cdb79451d30494461b091" - integrity sha512-yvRf8Ivk62JwisqV1rFRMxiSMDGnN6KH1/mDMmIrij4jztpQNRoHqqMG3U6apYbGRPJpgPalhva9Yd06HlUxJQ== - dependencies: - "@babel/types" "^7.13.0" - -"@babel/helper-module-imports@^7.12.1", "@babel/helper-module-imports@^7.12.13": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.12.13.tgz#ec67e4404f41750463e455cc3203f6a32e93fcb0" - integrity sha512-NGmfvRp9Rqxy0uHSSVP+SRIW1q31a7Ji10cLBcqSDUngGentY4FRiHOFZFE1CLU5eiL0oE8reH7Tg1y99TDM/g== - dependencies: - "@babel/types" "^7.12.13" - -"@babel/helper-module-transforms@^7.12.1", "@babel/helper-module-transforms@^7.13.0": - version "7.13.0" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.13.0.tgz#42eb4bd8eea68bab46751212c357bfed8b40f6f1" - integrity sha512-Ls8/VBwH577+pw7Ku1QkUWIyRRNHpYlts7+qSqBBFCW3I8QteB9DxfcZ5YJpOwH6Ihe/wn8ch7fMGOP1OhEIvw== - dependencies: - "@babel/helper-module-imports" "^7.12.13" - "@babel/helper-replace-supers" "^7.13.0" - "@babel/helper-simple-access" "^7.12.13" - "@babel/helper-split-export-declaration" "^7.12.13" - "@babel/helper-validator-identifier" "^7.12.11" - "@babel/template" "^7.12.13" - "@babel/traverse" "^7.13.0" - "@babel/types" "^7.13.0" - lodash "^4.17.19" - -"@babel/helper-optimise-call-expression@^7.12.13": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.12.13.tgz#5c02d171b4c8615b1e7163f888c1c81c30a2aaea" - integrity sha512-BdWQhoVJkp6nVjB7nkFWcn43dkprYauqtk++Py2eaf/GRDFm5BxRqEIZCiHlZUGAVmtwKcsVL1dC68WmzeFmiA== - dependencies: - "@babel/types" "^7.12.13" - -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.13.0", "@babel/helper-plugin-utils@^7.8.0", "@babel/helper-plugin-utils@^7.8.3": - version "7.13.0" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.13.0.tgz#806526ce125aed03373bc416a828321e3a6a33af" - integrity sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ== - -"@babel/helper-remap-async-to-generator@^7.13.0": - version "7.13.0" - resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.13.0.tgz#376a760d9f7b4b2077a9dd05aa9c3927cadb2209" - integrity sha512-pUQpFBE9JvC9lrQbpX0TmeNIy5s7GnZjna2lhhcHC7DzgBs6fWn722Y5cfwgrtrqc7NAJwMvOa0mKhq6XaE4jg== - dependencies: - "@babel/helper-annotate-as-pure" "^7.12.13" - "@babel/helper-wrap-function" "^7.13.0" - "@babel/types" "^7.13.0" - -"@babel/helper-replace-supers@^7.12.13", "@babel/helper-replace-supers@^7.13.0": - version "7.13.0" - resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.13.0.tgz#6034b7b51943094cb41627848cb219cb02be1d24" - integrity sha512-Segd5me1+Pz+rmN/NFBOplMbZG3SqRJOBlY+mA0SxAv6rjj7zJqr1AVr3SfzUVTLCv7ZLU5FycOM/SBGuLPbZw== - dependencies: - "@babel/helper-member-expression-to-functions" "^7.13.0" - "@babel/helper-optimise-call-expression" "^7.12.13" - "@babel/traverse" "^7.13.0" - "@babel/types" "^7.13.0" - -"@babel/helper-simple-access@^7.12.13": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.12.13.tgz#8478bcc5cacf6aa1672b251c1d2dde5ccd61a6c4" - integrity sha512-0ski5dyYIHEfwpWGx5GPWhH35j342JaflmCeQmsPWcrOQDtCN6C1zKAVRFVbK53lPW2c9TsuLLSUDf0tIGJ5hA== - dependencies: - "@babel/types" "^7.12.13" - -"@babel/helper-skip-transparent-expression-wrappers@^7.12.1": - version "7.12.1" - resolved "https://registry.yarnpkg.com/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.12.1.tgz#462dc63a7e435ade8468385c63d2b84cce4b3cbf" - integrity sha512-Mf5AUuhG1/OCChOJ/HcADmvcHM42WJockombn8ATJG3OnyiSxBK/Mm5x78BQWvmtXZKHgbjdGL2kin/HOLlZGA== - dependencies: - "@babel/types" "^7.12.1" - -"@babel/helper-split-export-declaration@^7.12.13": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz#e9430be00baf3e88b0e13e6f9d4eaf2136372b05" - integrity sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg== - dependencies: - "@babel/types" "^7.12.13" - -"@babel/helper-validator-identifier@^7.12.11": - version "7.12.11" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz#c9a1f021917dcb5ccf0d4e453e399022981fc9ed" - integrity sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw== - -"@babel/helper-validator-option@^7.12.1", "@babel/helper-validator-option@^7.12.17": - version "7.12.17" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.12.17.tgz#d1fbf012e1a79b7eebbfdc6d270baaf8d9eb9831" - integrity sha512-TopkMDmLzq8ngChwRlyjR6raKD6gMSae4JdYDB8bByKreQgG0RBTuKe9LRxW3wFtUnjxOPRKBDwEH6Mg5KeDfw== - -"@babel/helper-wrap-function@^7.13.0": - version "7.13.0" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.13.0.tgz#bdb5c66fda8526ec235ab894ad53a1235c79fcc4" - integrity sha512-1UX9F7K3BS42fI6qd2A4BjKzgGjToscyZTdp1DjknHLCIvpgne6918io+aL5LXFcER/8QWiwpoY902pVEqgTXA== - dependencies: - "@babel/helper-function-name" "^7.12.13" - "@babel/template" "^7.12.13" - "@babel/traverse" "^7.13.0" - "@babel/types" "^7.13.0" - -"@babel/helpers@^7.12.1", "@babel/helpers@^7.13.10": - version "7.13.10" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.13.10.tgz#fd8e2ba7488533cdeac45cc158e9ebca5e3c7df8" - integrity sha512-4VO883+MWPDUVRF3PhiLBUFHoX/bsLTGFpFK/HqvvfBZz2D57u9XzPVNFVBTc0PW/CWR9BXTOKt8NF4DInUHcQ== - dependencies: - "@babel/template" "^7.12.13" - "@babel/traverse" "^7.13.0" - "@babel/types" "^7.13.0" - -"@babel/highlight@^7.12.13": - version "7.13.10" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.13.10.tgz#a8b2a66148f5b27d666b15d81774347a731d52d1" - integrity sha512-5aPpe5XQPzflQrFwL1/QoeHkP2MsA4JCntcXHRhEsdsfPVkvPi2w7Qix4iV7t5S/oC9OodGrggd8aco1g3SZFg== - dependencies: - "@babel/helper-validator-identifier" "^7.12.11" - chalk "^2.0.0" - js-tokens "^4.0.0" - -"@babel/parser@^7.10.4", "@babel/parser@^7.12.13", "@babel/parser@^7.12.3", "@babel/parser@^7.13.0", "@babel/parser@^7.13.10": - version "7.13.10" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.13.10.tgz#8f8f9bf7b3afa3eabd061f7a5bcdf4fec3c48409" - integrity sha512-0s7Mlrw9uTWkYua7xWr99Wpk2bnGa0ANleKfksYAES8LpWH4gW1OUr42vqKNf0us5UQNfru2wPqMqRITzq/SIQ== - -"@babel/plugin-proposal-async-generator-functions@^7.12.1": - version "7.13.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.13.8.tgz#87aacb574b3bc4b5603f6fe41458d72a5a2ec4b1" - integrity sha512-rPBnhj+WgoSmgq+4gQUtXx/vOcU+UYtjy1AA/aeD61Hwj410fwYyqfUcRP3lR8ucgliVJL/G7sXcNUecC75IXA== - dependencies: - "@babel/helper-plugin-utils" "^7.13.0" - "@babel/helper-remap-async-to-generator" "^7.13.0" - "@babel/plugin-syntax-async-generators" "^7.8.4" - -"@babel/plugin-proposal-class-properties@^7.12.1": - version "7.13.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.13.0.tgz#146376000b94efd001e57a40a88a525afaab9f37" - integrity sha512-KnTDjFNC1g+45ka0myZNvSBFLhNCLN+GeGYLDEA8Oq7MZ6yMgfLoIRh86GRT0FjtJhZw8JyUskP9uvj5pHM9Zg== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.13.0" - "@babel/helper-plugin-utils" "^7.13.0" - -"@babel/plugin-proposal-dynamic-import@^7.12.1": - version "7.13.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-dynamic-import/-/plugin-proposal-dynamic-import-7.13.8.tgz#876a1f6966e1dec332e8c9451afda3bebcdf2e1d" - integrity sha512-ONWKj0H6+wIRCkZi9zSbZtE/r73uOhMVHh256ys0UzfM7I3d4n+spZNWjOnJv2gzopumP2Wxi186vI8N0Y2JyQ== - dependencies: - "@babel/helper-plugin-utils" "^7.13.0" - "@babel/plugin-syntax-dynamic-import" "^7.8.3" - -"@babel/plugin-proposal-export-namespace-from@^7.12.1": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-export-namespace-from/-/plugin-proposal-export-namespace-from-7.12.13.tgz#393be47a4acd03fa2af6e3cde9b06e33de1b446d" - integrity sha512-INAgtFo4OnLN3Y/j0VwAgw3HDXcDtX+C/erMvWzuV9v71r7urb6iyMXu7eM9IgLr1ElLlOkaHjJ0SbCmdOQ3Iw== - dependencies: - "@babel/helper-plugin-utils" "^7.12.13" - "@babel/plugin-syntax-export-namespace-from" "^7.8.3" - -"@babel/plugin-proposal-json-strings@^7.12.1": - version "7.13.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-json-strings/-/plugin-proposal-json-strings-7.13.8.tgz#bf1fb362547075afda3634ed31571c5901afef7b" - integrity sha512-w4zOPKUFPX1mgvTmL/fcEqy34hrQ1CRcGxdphBc6snDnnqJ47EZDIyop6IwXzAC8G916hsIuXB2ZMBCExC5k7Q== - dependencies: - "@babel/helper-plugin-utils" "^7.13.0" - "@babel/plugin-syntax-json-strings" "^7.8.3" - -"@babel/plugin-proposal-logical-assignment-operators@^7.12.1": - version "7.13.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-logical-assignment-operators/-/plugin-proposal-logical-assignment-operators-7.13.8.tgz#93fa78d63857c40ce3c8c3315220fd00bfbb4e1a" - integrity sha512-aul6znYB4N4HGweImqKn59Su9RS8lbUIqxtXTOcAGtNIDczoEFv+l1EhmX8rUBp3G1jMjKJm8m0jXVp63ZpS4A== - dependencies: - "@babel/helper-plugin-utils" "^7.13.0" - "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" - -"@babel/plugin-proposal-nullish-coalescing-operator@^7.12.1": - version "7.13.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-nullish-coalescing-operator/-/plugin-proposal-nullish-coalescing-operator-7.13.8.tgz#3730a31dafd3c10d8ccd10648ed80a2ac5472ef3" - integrity sha512-iePlDPBn//UhxExyS9KyeYU7RM9WScAG+D3Hhno0PLJebAEpDZMocbDe64eqynhNAnwz/vZoL/q/QB2T1OH39A== - dependencies: - "@babel/helper-plugin-utils" "^7.13.0" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" - -"@babel/plugin-proposal-numeric-separator@^7.12.1": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-numeric-separator/-/plugin-proposal-numeric-separator-7.12.13.tgz#bd9da3188e787b5120b4f9d465a8261ce67ed1db" - integrity sha512-O1jFia9R8BUCl3ZGB7eitaAPu62TXJRHn7rh+ojNERCFyqRwJMTmhz+tJ+k0CwI6CLjX/ee4qW74FSqlq9I35w== - dependencies: - "@babel/helper-plugin-utils" "^7.12.13" - "@babel/plugin-syntax-numeric-separator" "^7.10.4" - -"@babel/plugin-proposal-object-rest-spread@^7.12.1": - version "7.13.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-object-rest-spread/-/plugin-proposal-object-rest-spread-7.13.8.tgz#5d210a4d727d6ce3b18f9de82cc99a3964eed60a" - integrity sha512-DhB2EuB1Ih7S3/IRX5AFVgZ16k3EzfRbq97CxAVI1KSYcW+lexV8VZb7G7L8zuPVSdQMRn0kiBpf/Yzu9ZKH0g== - dependencies: - "@babel/compat-data" "^7.13.8" - "@babel/helper-compilation-targets" "^7.13.8" - "@babel/helper-plugin-utils" "^7.13.0" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-transform-parameters" "^7.13.0" - -"@babel/plugin-proposal-optional-catch-binding@^7.12.1": - version "7.13.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-catch-binding/-/plugin-proposal-optional-catch-binding-7.13.8.tgz#3ad6bd5901506ea996fc31bdcf3ccfa2bed71107" - integrity sha512-0wS/4DUF1CuTmGo+NiaHfHcVSeSLj5S3e6RivPTg/2k3wOv3jO35tZ6/ZWsQhQMvdgI7CwphjQa/ccarLymHVA== - dependencies: - "@babel/helper-plugin-utils" "^7.13.0" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" - -"@babel/plugin-proposal-optional-chaining@^7.12.1": - version "7.13.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-optional-chaining/-/plugin-proposal-optional-chaining-7.13.8.tgz#e39df93efe7e7e621841babc197982e140e90756" - integrity sha512-hpbBwbTgd7Cz1QryvwJZRo1U0k1q8uyBmeXOSQUjdg/A2TASkhR/rz7AyqZ/kS8kbpsNA80rOYbxySBJAqmhhQ== - dependencies: - "@babel/helper-plugin-utils" "^7.13.0" - "@babel/helper-skip-transparent-expression-wrappers" "^7.12.1" - "@babel/plugin-syntax-optional-chaining" "^7.8.3" - -"@babel/plugin-proposal-private-methods@^7.12.1": - version "7.13.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-methods/-/plugin-proposal-private-methods-7.13.0.tgz#04bd4c6d40f6e6bbfa2f57e2d8094bad900ef787" - integrity sha512-MXyyKQd9inhx1kDYPkFRVOBXQ20ES8Pto3T7UZ92xj2mY0EVD8oAVzeyYuVfy/mxAdTSIayOvg+aVzcHV2bn6Q== - dependencies: - "@babel/helper-create-class-features-plugin" "^7.13.0" - "@babel/helper-plugin-utils" "^7.13.0" - -"@babel/plugin-proposal-unicode-property-regex@^7.12.1", "@babel/plugin-proposal-unicode-property-regex@^7.4.4": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.12.13.tgz#bebde51339be829c17aaaaced18641deb62b39ba" - integrity sha512-XyJmZidNfofEkqFV5VC/bLabGmO5QzenPO/YOfGuEbgU+2sSwMmio3YLb4WtBgcmmdwZHyVyv8on77IUjQ5Gvg== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.12.13" - "@babel/helper-plugin-utils" "^7.12.13" - -"@babel/plugin-syntax-async-generators@^7.8.0", "@babel/plugin-syntax-async-generators@^7.8.4": - version "7.8.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" - integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-class-properties@^7.12.1": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" - integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== - dependencies: - "@babel/helper-plugin-utils" "^7.12.13" - -"@babel/plugin-syntax-dynamic-import@^7.8.0", "@babel/plugin-syntax-dynamic-import@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-dynamic-import/-/plugin-syntax-dynamic-import-7.8.3.tgz#62bf98b2da3cd21d626154fc96ee5b3cb68eacb3" - integrity sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-export-namespace-from@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-export-namespace-from/-/plugin-syntax-export-namespace-from-7.8.3.tgz#028964a9ba80dbc094c915c487ad7c4e7a66465a" - integrity sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q== - dependencies: - "@babel/helper-plugin-utils" "^7.8.3" - -"@babel/plugin-syntax-json-strings@^7.8.0", "@babel/plugin-syntax-json-strings@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" - integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-logical-assignment-operators@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" - integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.0", "@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" - integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-numeric-separator@^7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" - integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-syntax-object-rest-spread@^7.8.0", "@babel/plugin-syntax-object-rest-spread@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" - integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-optional-catch-binding@^7.8.0", "@babel/plugin-syntax-optional-catch-binding@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" - integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-optional-chaining@^7.8.0", "@babel/plugin-syntax-optional-chaining@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" - integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-top-level-await@^7.12.1": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.12.13.tgz#c5f0fa6e249f5b739727f923540cf7a806130178" - integrity sha512-A81F9pDwyS7yM//KwbCSDqy3Uj4NMIurtplxphWxoYtNPov7cJsDkAFNNyVlIZ3jwGycVsurZ+LtOA8gZ376iQ== - dependencies: - "@babel/helper-plugin-utils" "^7.12.13" - -"@babel/plugin-transform-arrow-functions@^7.12.1": - version "7.13.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.13.0.tgz#10a59bebad52d637a027afa692e8d5ceff5e3dae" - integrity sha512-96lgJagobeVmazXFaDrbmCLQxBysKu7U6Do3mLsx27gf5Dk85ezysrs2BZUpXD703U/Su1xTBDxxar2oa4jAGg== - dependencies: - "@babel/helper-plugin-utils" "^7.13.0" - -"@babel/plugin-transform-async-to-generator@^7.12.1": - version "7.13.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.13.0.tgz#8e112bf6771b82bf1e974e5e26806c5c99aa516f" - integrity sha512-3j6E004Dx0K3eGmhxVJxwwI89CTJrce7lg3UrtFuDAVQ/2+SJ/h/aSFOeE6/n0WB1GsOffsJp6MnPQNQ8nmwhg== - dependencies: - "@babel/helper-module-imports" "^7.12.13" - "@babel/helper-plugin-utils" "^7.13.0" - "@babel/helper-remap-async-to-generator" "^7.13.0" - -"@babel/plugin-transform-block-scoped-functions@^7.12.1": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoped-functions/-/plugin-transform-block-scoped-functions-7.12.13.tgz#a9bf1836f2a39b4eb6cf09967739de29ea4bf4c4" - integrity sha512-zNyFqbc3kI/fVpqwfqkg6RvBgFpC4J18aKKMmv7KdQ/1GgREapSJAykLMVNwfRGO3BtHj3YQZl8kxCXPcVMVeg== - dependencies: - "@babel/helper-plugin-utils" "^7.12.13" - -"@babel/plugin-transform-block-scoping@^7.12.1": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.12.13.tgz#f36e55076d06f41dfd78557ea039c1b581642e61" - integrity sha512-Pxwe0iqWJX4fOOM2kEZeUuAxHMWb9nK+9oh5d11bsLoB0xMg+mkDpt0eYuDZB7ETrY9bbcVlKUGTOGWy7BHsMQ== - dependencies: - "@babel/helper-plugin-utils" "^7.12.13" - -"@babel/plugin-transform-classes@^7.12.1": - version "7.13.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.13.0.tgz#0265155075c42918bf4d3a4053134176ad9b533b" - integrity sha512-9BtHCPUARyVH1oXGcSJD3YpsqRLROJx5ZNP6tN5vnk17N0SVf9WCtf8Nuh1CFmgByKKAIMstitKduoCmsaDK5g== - dependencies: - "@babel/helper-annotate-as-pure" "^7.12.13" - "@babel/helper-function-name" "^7.12.13" - "@babel/helper-optimise-call-expression" "^7.12.13" - "@babel/helper-plugin-utils" "^7.13.0" - "@babel/helper-replace-supers" "^7.13.0" - "@babel/helper-split-export-declaration" "^7.12.13" - globals "^11.1.0" - -"@babel/plugin-transform-computed-properties@^7.12.1": - version "7.13.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-computed-properties/-/plugin-transform-computed-properties-7.13.0.tgz#845c6e8b9bb55376b1fa0b92ef0bdc8ea06644ed" - integrity sha512-RRqTYTeZkZAz8WbieLTvKUEUxZlUTdmL5KGMyZj7FnMfLNKV4+r5549aORG/mgojRmFlQMJDUupwAMiF2Q7OUg== - dependencies: - "@babel/helper-plugin-utils" "^7.13.0" - -"@babel/plugin-transform-destructuring@^7.12.1": - version "7.13.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.13.0.tgz#c5dce270014d4e1ebb1d806116694c12b7028963" - integrity sha512-zym5em7tePoNT9s964c0/KU3JPPnuq7VhIxPRefJ4/s82cD+q1mgKfuGRDMCPL0HTyKz4dISuQlCusfgCJ86HA== - dependencies: - "@babel/helper-plugin-utils" "^7.13.0" - -"@babel/plugin-transform-dotall-regex@^7.12.1", "@babel/plugin-transform-dotall-regex@^7.4.4": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.12.13.tgz#3f1601cc29905bfcb67f53910f197aeafebb25ad" - integrity sha512-foDrozE65ZFdUC2OfgeOCrEPTxdB3yjqxpXh8CH+ipd9CHd4s/iq81kcUpyH8ACGNEPdFqbtzfgzbT/ZGlbDeQ== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.12.13" - "@babel/helper-plugin-utils" "^7.12.13" - -"@babel/plugin-transform-duplicate-keys@^7.12.1": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-duplicate-keys/-/plugin-transform-duplicate-keys-7.12.13.tgz#6f06b87a8b803fd928e54b81c258f0a0033904de" - integrity sha512-NfADJiiHdhLBW3pulJlJI2NB0t4cci4WTZ8FtdIuNc2+8pslXdPtRRAEWqUY+m9kNOk2eRYbTAOipAxlrOcwwQ== - dependencies: - "@babel/helper-plugin-utils" "^7.12.13" - -"@babel/plugin-transform-exponentiation-operator@^7.12.1": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-exponentiation-operator/-/plugin-transform-exponentiation-operator-7.12.13.tgz#4d52390b9a273e651e4aba6aee49ef40e80cd0a1" - integrity sha512-fbUelkM1apvqez/yYx1/oICVnGo2KM5s63mhGylrmXUxK/IAXSIf87QIxVfZldWf4QsOafY6vV3bX8aMHSvNrA== - dependencies: - "@babel/helper-builder-binary-assignment-operator-visitor" "^7.12.13" - "@babel/helper-plugin-utils" "^7.12.13" - -"@babel/plugin-transform-for-of@^7.12.1": - version "7.13.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.13.0.tgz#c799f881a8091ac26b54867a845c3e97d2696062" - integrity sha512-IHKT00mwUVYE0zzbkDgNRP6SRzvfGCYsOxIRz8KsiaaHCcT9BWIkO+H9QRJseHBLOGBZkHUdHiqj6r0POsdytg== - dependencies: - "@babel/helper-plugin-utils" "^7.13.0" - -"@babel/plugin-transform-function-name@^7.12.1": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-function-name/-/plugin-transform-function-name-7.12.13.tgz#bb024452f9aaed861d374c8e7a24252ce3a50051" - integrity sha512-6K7gZycG0cmIwwF7uMK/ZqeCikCGVBdyP2J5SKNCXO5EOHcqi+z7Jwf8AmyDNcBgxET8DrEtCt/mPKPyAzXyqQ== - dependencies: - "@babel/helper-function-name" "^7.12.13" - "@babel/helper-plugin-utils" "^7.12.13" - -"@babel/plugin-transform-literals@^7.12.1": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-literals/-/plugin-transform-literals-7.12.13.tgz#2ca45bafe4a820197cf315794a4d26560fe4bdb9" - integrity sha512-FW+WPjSR7hiUxMcKqyNjP05tQ2kmBCdpEpZHY1ARm96tGQCCBvXKnpjILtDplUnJ/eHZ0lALLM+d2lMFSpYJrQ== - dependencies: - "@babel/helper-plugin-utils" "^7.12.13" - -"@babel/plugin-transform-member-expression-literals@^7.12.1": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-member-expression-literals/-/plugin-transform-member-expression-literals-7.12.13.tgz#5ffa66cd59b9e191314c9f1f803b938e8c081e40" - integrity sha512-kxLkOsg8yir4YeEPHLuO2tXP9R/gTjpuTOjshqSpELUN3ZAg2jfDnKUvzzJxObun38sw3wm4Uu69sX/zA7iRvg== - dependencies: - "@babel/helper-plugin-utils" "^7.12.13" - -"@babel/plugin-transform-modules-amd@^7.12.1": - version "7.13.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-amd/-/plugin-transform-modules-amd-7.13.0.tgz#19f511d60e3d8753cc5a6d4e775d3a5184866cc3" - integrity sha512-EKy/E2NHhY/6Vw5d1k3rgoobftcNUmp9fGjb9XZwQLtTctsRBOTRO7RHHxfIky1ogMN5BxN7p9uMA3SzPfotMQ== - dependencies: - "@babel/helper-module-transforms" "^7.13.0" - "@babel/helper-plugin-utils" "^7.13.0" - babel-plugin-dynamic-import-node "^2.3.3" - -"@babel/plugin-transform-modules-commonjs@^7.12.1": - version "7.13.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.13.8.tgz#7b01ad7c2dcf2275b06fa1781e00d13d420b3e1b" - integrity sha512-9QiOx4MEGglfYZ4XOnU79OHr6vIWUakIj9b4mioN8eQIoEh+pf5p/zEB36JpDFWA12nNMiRf7bfoRvl9Rn79Bw== - dependencies: - "@babel/helper-module-transforms" "^7.13.0" - "@babel/helper-plugin-utils" "^7.13.0" - "@babel/helper-simple-access" "^7.12.13" - babel-plugin-dynamic-import-node "^2.3.3" - -"@babel/plugin-transform-modules-systemjs@^7.12.1": - version "7.13.8" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-systemjs/-/plugin-transform-modules-systemjs-7.13.8.tgz#6d066ee2bff3c7b3d60bf28dec169ad993831ae3" - integrity sha512-hwqctPYjhM6cWvVIlOIe27jCIBgHCsdH2xCJVAYQm7V5yTMoilbVMi9f6wKg0rpQAOn6ZG4AOyvCqFF/hUh6+A== - dependencies: - "@babel/helper-hoist-variables" "^7.13.0" - "@babel/helper-module-transforms" "^7.13.0" - "@babel/helper-plugin-utils" "^7.13.0" - "@babel/helper-validator-identifier" "^7.12.11" - babel-plugin-dynamic-import-node "^2.3.3" - -"@babel/plugin-transform-modules-umd@^7.12.1": - version "7.13.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-umd/-/plugin-transform-modules-umd-7.13.0.tgz#8a3d96a97d199705b9fd021580082af81c06e70b" - integrity sha512-D/ILzAh6uyvkWjKKyFE/W0FzWwasv6vPTSqPcjxFqn6QpX3u8DjRVliq4F2BamO2Wee/om06Vyy+vPkNrd4wxw== - dependencies: - "@babel/helper-module-transforms" "^7.13.0" - "@babel/helper-plugin-utils" "^7.13.0" - -"@babel/plugin-transform-named-capturing-groups-regex@^7.12.1": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.12.13.tgz#2213725a5f5bbbe364b50c3ba5998c9599c5c9d9" - integrity sha512-Xsm8P2hr5hAxyYblrfACXpQKdQbx4m2df9/ZZSQ8MAhsadw06+jW7s9zsSw6he+mJZXRlVMyEnVktJo4zjk1WA== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.12.13" - -"@babel/plugin-transform-new-target@^7.12.1": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-new-target/-/plugin-transform-new-target-7.12.13.tgz#e22d8c3af24b150dd528cbd6e685e799bf1c351c" - integrity sha512-/KY2hbLxrG5GTQ9zzZSc3xWiOy379pIETEhbtzwZcw9rvuaVV4Fqy7BYGYOWZnaoXIQYbbJ0ziXLa/sKcGCYEQ== - dependencies: - "@babel/helper-plugin-utils" "^7.12.13" - -"@babel/plugin-transform-object-super@^7.12.1": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.12.13.tgz#b4416a2d63b8f7be314f3d349bd55a9c1b5171f7" - integrity sha512-JzYIcj3XtYspZDV8j9ulnoMPZZnF/Cj0LUxPOjR89BdBVx+zYJI9MdMIlUZjbXDX+6YVeS6I3e8op+qQ3BYBoQ== - dependencies: - "@babel/helper-plugin-utils" "^7.12.13" - "@babel/helper-replace-supers" "^7.12.13" - -"@babel/plugin-transform-parameters@^7.12.1", "@babel/plugin-transform-parameters@^7.13.0": - version "7.13.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.13.0.tgz#8fa7603e3097f9c0b7ca1a4821bc2fb52e9e5007" - integrity sha512-Jt8k/h/mIwE2JFEOb3lURoY5C85ETcYPnbuAJ96zRBzh1XHtQZfs62ChZ6EP22QlC8c7Xqr9q+e1SU5qttwwjw== - dependencies: - "@babel/helper-plugin-utils" "^7.13.0" - -"@babel/plugin-transform-property-literals@^7.12.1": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-property-literals/-/plugin-transform-property-literals-7.12.13.tgz#4e6a9e37864d8f1b3bc0e2dce7bf8857db8b1a81" - integrity sha512-nqVigwVan+lR+g8Fj8Exl0UQX2kymtjcWfMOYM1vTYEKujeyv2SkMgazf2qNcK7l4SDiKyTA/nHCPqL4e2zo1A== - dependencies: - "@babel/helper-plugin-utils" "^7.12.13" - -"@babel/plugin-transform-regenerator@^7.12.1": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.12.13.tgz#b628bcc9c85260ac1aeb05b45bde25210194a2f5" - integrity sha512-lxb2ZAvSLyJ2PEe47hoGWPmW22v7CtSl9jW8mingV4H2sEX/JOcrAj2nPuGWi56ERUm2bUpjKzONAuT6HCn2EA== - dependencies: - regenerator-transform "^0.14.2" - -"@babel/plugin-transform-reserved-words@^7.12.1": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-reserved-words/-/plugin-transform-reserved-words-7.12.13.tgz#7d9988d4f06e0fe697ea1d9803188aa18b472695" - integrity sha512-xhUPzDXxZN1QfiOy/I5tyye+TRz6lA7z6xaT4CLOjPRMVg1ldRf0LHw0TDBpYL4vG78556WuHdyO9oi5UmzZBg== - dependencies: - "@babel/helper-plugin-utils" "^7.12.13" - -"@babel/plugin-transform-runtime@7.12.1": - version "7.12.1" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.12.1.tgz#04b792057eb460389ff6a4198e377614ea1e7ba5" - integrity sha512-Ac/H6G9FEIkS2tXsZjL4RAdS3L3WHxci0usAnz7laPWUmFiGtj7tIASChqKZMHTSQTQY6xDbOq+V1/vIq3QrWg== - dependencies: - "@babel/helper-module-imports" "^7.12.1" - "@babel/helper-plugin-utils" "^7.10.4" - resolve "^1.8.1" - semver "^5.5.1" - -"@babel/plugin-transform-shorthand-properties@^7.12.1": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.12.13.tgz#db755732b70c539d504c6390d9ce90fe64aff7ad" - integrity sha512-xpL49pqPnLtf0tVluuqvzWIgLEhuPpZzvs2yabUHSKRNlN7ScYU7aMlmavOeyXJZKgZKQRBlh8rHbKiJDraTSw== - dependencies: - "@babel/helper-plugin-utils" "^7.12.13" - -"@babel/plugin-transform-spread@^7.12.1": - version "7.13.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-spread/-/plugin-transform-spread-7.13.0.tgz#84887710e273c1815ace7ae459f6f42a5d31d5fd" - integrity sha512-V6vkiXijjzYeFmQTr3dBxPtZYLPcUfY34DebOU27jIl2M/Y8Egm52Hw82CSjjPqd54GTlJs5x+CR7HeNr24ckg== - dependencies: - "@babel/helper-plugin-utils" "^7.13.0" - "@babel/helper-skip-transparent-expression-wrappers" "^7.12.1" - -"@babel/plugin-transform-sticky-regex@^7.12.1": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-sticky-regex/-/plugin-transform-sticky-regex-7.12.13.tgz#760ffd936face73f860ae646fb86ee82f3d06d1f" - integrity sha512-Jc3JSaaWT8+fr7GRvQP02fKDsYk4K/lYwWq38r/UGfaxo89ajud321NH28KRQ7xy1Ybc0VUE5Pz8psjNNDUglg== - dependencies: - "@babel/helper-plugin-utils" "^7.12.13" - -"@babel/plugin-transform-template-literals@^7.12.1": - version "7.13.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.13.0.tgz#a36049127977ad94438dee7443598d1cefdf409d" - integrity sha512-d67umW6nlfmr1iehCcBv69eSUSySk1EsIS8aTDX4Xo9qajAh6mYtcl4kJrBkGXuxZPEgVr7RVfAvNW6YQkd4Mw== - dependencies: - "@babel/helper-plugin-utils" "^7.13.0" - -"@babel/plugin-transform-typeof-symbol@^7.12.1": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typeof-symbol/-/plugin-transform-typeof-symbol-7.12.13.tgz#785dd67a1f2ea579d9c2be722de8c84cb85f5a7f" - integrity sha512-eKv/LmUJpMnu4npgfvs3LiHhJua5fo/CysENxa45YCQXZwKnGCQKAg87bvoqSW1fFT+HA32l03Qxsm8ouTY3ZQ== - dependencies: - "@babel/helper-plugin-utils" "^7.12.13" - -"@babel/plugin-transform-unicode-escapes@^7.12.1": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.12.13.tgz#840ced3b816d3b5127dd1d12dcedc5dead1a5e74" - integrity sha512-0bHEkdwJ/sN/ikBHfSmOXPypN/beiGqjo+o4/5K+vxEFNPRPdImhviPakMKG4x96l85emoa0Z6cDflsdBusZbw== - dependencies: - "@babel/helper-plugin-utils" "^7.12.13" - -"@babel/plugin-transform-unicode-regex@^7.12.1": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.12.13.tgz#b52521685804e155b1202e83fc188d34bb70f5ac" - integrity sha512-mDRzSNY7/zopwisPZ5kM9XKCfhchqIYwAKRERtEnhYscZB79VRekuRSoYbN0+KVe3y8+q1h6A4svXtP7N+UoCA== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.12.13" - "@babel/helper-plugin-utils" "^7.12.13" - -"@babel/preset-env@7.12.1": - version "7.12.1" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.12.1.tgz#9c7e5ca82a19efc865384bb4989148d2ee5d7ac2" - integrity sha512-H8kxXmtPaAGT7TyBvSSkoSTUK6RHh61So05SyEbpmr0MCZrsNYn7mGMzzeYoOUCdHzww61k8XBft2TaES+xPLg== - dependencies: - "@babel/compat-data" "^7.12.1" - "@babel/helper-compilation-targets" "^7.12.1" - "@babel/helper-module-imports" "^7.12.1" - "@babel/helper-plugin-utils" "^7.10.4" - "@babel/helper-validator-option" "^7.12.1" - "@babel/plugin-proposal-async-generator-functions" "^7.12.1" - "@babel/plugin-proposal-class-properties" "^7.12.1" - "@babel/plugin-proposal-dynamic-import" "^7.12.1" - "@babel/plugin-proposal-export-namespace-from" "^7.12.1" - "@babel/plugin-proposal-json-strings" "^7.12.1" - "@babel/plugin-proposal-logical-assignment-operators" "^7.12.1" - "@babel/plugin-proposal-nullish-coalescing-operator" "^7.12.1" - "@babel/plugin-proposal-numeric-separator" "^7.12.1" - "@babel/plugin-proposal-object-rest-spread" "^7.12.1" - "@babel/plugin-proposal-optional-catch-binding" "^7.12.1" - "@babel/plugin-proposal-optional-chaining" "^7.12.1" - "@babel/plugin-proposal-private-methods" "^7.12.1" - "@babel/plugin-proposal-unicode-property-regex" "^7.12.1" - "@babel/plugin-syntax-async-generators" "^7.8.0" - "@babel/plugin-syntax-class-properties" "^7.12.1" - "@babel/plugin-syntax-dynamic-import" "^7.8.0" - "@babel/plugin-syntax-export-namespace-from" "^7.8.3" - "@babel/plugin-syntax-json-strings" "^7.8.0" - "@babel/plugin-syntax-logical-assignment-operators" "^7.10.4" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.0" - "@babel/plugin-syntax-numeric-separator" "^7.10.4" - "@babel/plugin-syntax-object-rest-spread" "^7.8.0" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.0" - "@babel/plugin-syntax-optional-chaining" "^7.8.0" - "@babel/plugin-syntax-top-level-await" "^7.12.1" - "@babel/plugin-transform-arrow-functions" "^7.12.1" - "@babel/plugin-transform-async-to-generator" "^7.12.1" - "@babel/plugin-transform-block-scoped-functions" "^7.12.1" - "@babel/plugin-transform-block-scoping" "^7.12.1" - "@babel/plugin-transform-classes" "^7.12.1" - "@babel/plugin-transform-computed-properties" "^7.12.1" - "@babel/plugin-transform-destructuring" "^7.12.1" - "@babel/plugin-transform-dotall-regex" "^7.12.1" - "@babel/plugin-transform-duplicate-keys" "^7.12.1" - "@babel/plugin-transform-exponentiation-operator" "^7.12.1" - "@babel/plugin-transform-for-of" "^7.12.1" - "@babel/plugin-transform-function-name" "^7.12.1" - "@babel/plugin-transform-literals" "^7.12.1" - "@babel/plugin-transform-member-expression-literals" "^7.12.1" - "@babel/plugin-transform-modules-amd" "^7.12.1" - "@babel/plugin-transform-modules-commonjs" "^7.12.1" - "@babel/plugin-transform-modules-systemjs" "^7.12.1" - "@babel/plugin-transform-modules-umd" "^7.12.1" - "@babel/plugin-transform-named-capturing-groups-regex" "^7.12.1" - "@babel/plugin-transform-new-target" "^7.12.1" - "@babel/plugin-transform-object-super" "^7.12.1" - "@babel/plugin-transform-parameters" "^7.12.1" - "@babel/plugin-transform-property-literals" "^7.12.1" - "@babel/plugin-transform-regenerator" "^7.12.1" - "@babel/plugin-transform-reserved-words" "^7.12.1" - "@babel/plugin-transform-shorthand-properties" "^7.12.1" - "@babel/plugin-transform-spread" "^7.12.1" - "@babel/plugin-transform-sticky-regex" "^7.12.1" - "@babel/plugin-transform-template-literals" "^7.12.1" - "@babel/plugin-transform-typeof-symbol" "^7.12.1" - "@babel/plugin-transform-unicode-escapes" "^7.12.1" - "@babel/plugin-transform-unicode-regex" "^7.12.1" - "@babel/preset-modules" "^0.1.3" - "@babel/types" "^7.12.1" - core-js-compat "^3.6.2" - semver "^5.5.0" - -"@babel/preset-modules@^0.1.3": - version "0.1.4" - resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.4.tgz#362f2b68c662842970fdb5e254ffc8fc1c2e415e" - integrity sha512-J36NhwnfdzpmH41M1DrnkkgAqhZaqr/NBdPfQ677mLzlaXo+oDiv1deyCDtgAhz8p328otdob0Du7+xgHGZbKg== - 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" - -"@babel/runtime@7.12.1": - version "7.12.1" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.1.tgz#b4116a6b6711d010b2dad3b7b6e43bf1b9954740" - integrity sha512-J5AIf3vPj3UwXaAzb5j1xM4WAQDX3EMgemF8rjCP3SoW09LfRKAXQKt6CoVYl230P6iWdRcBbnLDDdnqWxZSCA== - dependencies: - regenerator-runtime "^0.13.4" - -"@babel/runtime@^7.8.4": - version "7.13.10" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.13.10.tgz#47d42a57b6095f4468da440388fdbad8bebf0d7d" - integrity sha512-4QPkjJq6Ns3V/RgpEahRk+AGfL0eO6RHHtTWoNNr5mO49G6B5+X6d6THgWEAvTrznU5xYpbAlVKRYcsCgh/Akw== - dependencies: - regenerator-runtime "^0.13.4" - -"@babel/template@7.10.4": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.4.tgz#3251996c4200ebc71d1a8fc405fba940f36ba278" - integrity sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA== - dependencies: - "@babel/code-frame" "^7.10.4" - "@babel/parser" "^7.10.4" - "@babel/types" "^7.10.4" - -"@babel/template@^7.10.4", "@babel/template@^7.12.13": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.12.13.tgz#530265be8a2589dbb37523844c5bcb55947fb327" - integrity sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA== - dependencies: - "@babel/code-frame" "^7.12.13" - "@babel/parser" "^7.12.13" - "@babel/types" "^7.12.13" - -"@babel/traverse@^7.12.1", "@babel/traverse@^7.13.0": - version "7.13.0" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.13.0.tgz#6d95752475f86ee7ded06536de309a65fc8966cc" - integrity sha512-xys5xi5JEhzC3RzEmSGrs/b3pJW/o87SypZ+G/PhaE7uqVQNv/jlmVIBXuoh5atqQ434LfXV+sf23Oxj0bchJQ== - dependencies: - "@babel/code-frame" "^7.12.13" - "@babel/generator" "^7.13.0" - "@babel/helper-function-name" "^7.12.13" - "@babel/helper-split-export-declaration" "^7.12.13" - "@babel/parser" "^7.13.0" - "@babel/types" "^7.13.0" - debug "^4.1.0" - globals "^11.1.0" - lodash "^4.17.19" - -"@babel/types@^7.10.4", "@babel/types@^7.12.1", "@babel/types@^7.12.13", "@babel/types@^7.13.0", "@babel/types@^7.4.4", "@babel/types@^7.8.6": - version "7.13.0" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.13.0.tgz#74424d2816f0171b4100f0ab34e9a374efdf7f80" - integrity sha512-hE+HE8rnG1Z6Wzo+MhaKE5lM5eMx71T4EHJgku2E3xIfaULhDcxiiRxUYgwX8qwP1BBSlag+TdGOt6JAidIZTA== - dependencies: - "@babel/helper-validator-identifier" "^7.12.11" - lodash "^4.17.19" - to-fast-properties "^2.0.0" - -"@electron/get@^1.0.1": - version "1.12.4" - resolved "https://registry.yarnpkg.com/@electron/get/-/get-1.12.4.tgz#a5971113fc1bf8fa12a8789dc20152a7359f06ab" - integrity sha512-6nr9DbJPUR9Xujw6zD3y+rS95TyItEVM0NVjt1EehY2vUWfIgPiIPVHxCvaTS0xr2B+DRxovYVKbuOWqC35kjg== - dependencies: - debug "^4.1.1" - env-paths "^2.2.0" - fs-extra "^8.1.0" - got "^9.6.0" - progress "^2.0.3" - semver "^6.2.0" - sumchecker "^3.0.1" - optionalDependencies: - global-agent "^2.0.2" - global-tunnel-ng "^2.7.1" - -"@istanbuljs/schema@^0.1.2": - version "0.1.3" - resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" - integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== - -"@jsdevtools/coverage-istanbul-loader@3.0.5": - version "3.0.5" - resolved "https://registry.yarnpkg.com/@jsdevtools/coverage-istanbul-loader/-/coverage-istanbul-loader-3.0.5.tgz#2a4bc65d0271df8d4435982db4af35d81754ee26" - integrity sha512-EUCPEkaRPvmHjWAAZkWMT7JDzpw7FKB00WTISaiXsbNOd5hCHg77XLA8sLYLFDo1zepYLo2w7GstN8YBqRXZfA== - dependencies: - convert-source-map "^1.7.0" - istanbul-lib-instrument "^4.0.3" - loader-utils "^2.0.0" - merge-source-map "^1.1.0" - schema-utils "^2.7.0" - -"@mdi/angular-material@^5.8.55": - version "5.9.55" - resolved "https://registry.yarnpkg.com/@mdi/angular-material/-/angular-material-5.9.55.tgz#e4fad2ae73d630f0853dc67e509ca50ea0cbef03" - integrity sha512-RAQlvSI90eJHW9o6uSN5KKH8SJ61ZQuXVCjLDpQWtKQX8aTRPTJkruZ1M2U0YT6AtxKY4hPW7qyHH7VTJdTXRA== - -"@ngtools/webpack@11.0.7": - version "11.0.7" - resolved "https://registry.yarnpkg.com/@ngtools/webpack/-/webpack-11.0.7.tgz#3ec8cd967fdf1dfc79e6b1469369148bc3a93bd1" - integrity sha512-OWGiiDc7s4T53BBCY8tLkLUjgw44HrixW8Wh8e4thFH1eIUM0NHe087s/B5hDNu72W/GqK4IoBbhNQ2wiCR7qQ== - dependencies: - "@angular-devkit/core" "11.0.7" - enhanced-resolve "5.3.1" - webpack-sources "2.0.1" - -"@nodelib/fs.scandir@2.1.4": - version "2.1.4" - resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.4.tgz#d4b3549a5db5de2683e0c1071ab4f140904bbf69" - integrity sha512-33g3pMJk3bg5nXbL/+CY6I2eJDzZAni49PfJnL5fghPTggPvBd/pFNSgJsdAgWptuFu7qq/ERvOYFlhvsLTCKA== - dependencies: - "@nodelib/fs.stat" "2.0.4" - run-parallel "^1.1.9" - -"@nodelib/fs.stat@2.0.4", "@nodelib/fs.stat@^2.0.2": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.4.tgz#a3f2dd61bab43b8db8fa108a121cfffe4c676655" - integrity sha512-IYlHJA0clt2+Vg7bccq+TzRdJvv19c2INqBSsoOLp1je7xjtr7J26+WXR72MCdvU9q1qTzIWDfhMf+DRvQJK4Q== - -"@nodelib/fs.walk@^1.2.3": - version "1.2.6" - resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.6.tgz#cce9396b30aa5afe9e3756608f5831adcb53d063" - integrity sha512-8Broas6vTtW4GIXTAHDoE32hnN2M5ykgCpWGbuXHQ15vEMqr23pB76e/GZcYsZCHALv50ktd24qhEyKr6wBtow== - dependencies: - "@nodelib/fs.scandir" "2.1.4" - fastq "^1.6.0" - -"@npmcli/move-file@^1.0.1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-1.1.2.tgz#1a82c3e372f7cae9253eb66d72543d6b8685c674" - integrity sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg== - dependencies: - mkdirp "^1.0.4" - rimraf "^3.0.2" - -"@schematics/angular@11.0.6": - version "11.0.6" - resolved "https://registry.yarnpkg.com/@schematics/angular/-/angular-11.0.6.tgz#5e52f8396e66138df0d6062130399fab830ee79e" - integrity sha512-XUcpOrlcp55PBHrgpIVx69lnhDY6ro35BSRmqNmjXik56qcOkfvdki8vvyW9EsWvu9/sfBSsVDdparlbVois7w== - dependencies: - "@angular-devkit/core" "11.0.6" - "@angular-devkit/schematics" "11.0.6" - jsonc-parser "2.3.1" - -"@schematics/update@0.1100.6": - version "0.1100.6" - resolved "https://registry.yarnpkg.com/@schematics/update/-/update-0.1100.6.tgz#8e76276a3daecfd698b39e7643bc21f3abb3a4d0" - integrity sha512-+B8n+k+zZ3VYOhjNBsLqzjp8O9ZdUWgdpf9L8XAA7mh/oPwufXpExyEc66uAS07imvUMmjz6i8E2eNWV/IjBJg== - dependencies: - "@angular-devkit/core" "11.0.6" - "@angular-devkit/schematics" "11.0.6" - "@yarnpkg/lockfile" "1.1.0" - ini "1.3.6" - npm-package-arg "^8.0.0" - pacote "9.5.12" - semver "7.3.2" - semver-intersect "1.4.0" - -"@sindresorhus/is@^0.14.0": - version "0.14.0" - resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz#9fb3a3cf3132328151f353de4632e01e52102bea" - integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== - -"@stomp/rx-stomp@^0.3.5": - version "0.3.5" - resolved "https://registry.yarnpkg.com/@stomp/rx-stomp/-/rx-stomp-0.3.5.tgz#3739f44f3f098ddfaf4f46d1f3b6541e3a1d4224" - integrity sha512-oEaMkq2IejzVKFg8EtSTe0xnyhPVWJxuQmnuTQStC3EAPcJgH9eQziJc0BR9tUvm6m962v4LPvyJKlzxOJhFWg== - dependencies: - "@stomp/stompjs" "^5.1.0 >=5.4.2" - angular2-uuid "^1.1.1" - -"@stomp/stompjs@^5.1.0 >=5.4.2": - version "5.4.4" - resolved "https://registry.yarnpkg.com/@stomp/stompjs/-/stompjs-5.4.4.tgz#f51d2edf9a00fac645dde3a494738d96ca17e5aa" - integrity sha512-RIzQ7MLRSJLUpTHcje1ZclnHH982amJSKC9bDxGO0wyu5OF9ROuuiLf7TxKxo1zUu7lGEYNedg9SEi87uMWDqg== - -"@szmarczak/http-timer@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz#b1665e2c461a2cd92f4c1bbf50d5454de0d4b421" - integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA== - dependencies: - defer-to-connect "^1.0.1" - -"@types/glob@^7.1.1": - version "7.1.3" - resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.1.3.tgz#e6ba80f36b7daad2c685acd9266382e68985c183" - integrity sha512-SEYeGAIQIQX8NN6LDKprLjbrd5dARM5EXsd8GI/A5l0apYI1fGMWgPHSe4ZKL4eozlAyI+doUE9XbYS4xCkQ1w== - dependencies: - "@types/minimatch" "*" - "@types/node" "*" - -"@types/jasmine@*": - version "3.6.6" - resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-3.6.6.tgz#4c0415f6198b2c643d895789f8c6d20436c1eddc" - integrity sha512-kgl+oYOLCBt41iba8cetp+QPOqDAaTJnHtVPCE7JzYmda4juglRBLX35opVcANc7TLA/Lp0DEnajbUNnyxGC+Q== - -"@types/jasmine@3.5.12": - version "3.5.12" - resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-3.5.12.tgz#5c378c1545cdc7cb339cff5578f854b6d1e0a17d" - integrity sha512-vJaQ58oceFao+NzpKNqLOWwHPsqA7YEhKv+mOXvYU4/qh+BfVWIxaBtL0Ck5iCS67yOkNwGkDCrzepnzIWF+7g== - -"@types/jasminewd2@2.0.8": - version "2.0.8" - resolved "https://registry.yarnpkg.com/@types/jasminewd2/-/jasminewd2-2.0.8.tgz#67afe5098d5ef2386073a7b7384b69a840dfe93b" - integrity sha512-d9p31r7Nxk0ZH0U39PTH0hiDlJ+qNVGjlt1ucOoTUptxb2v+Y5VMnsxfwN+i3hK4yQnqBi3FMmoMFcd1JHDxdg== - dependencies: - "@types/jasmine" "*" - -"@types/json-schema@^7.0.5", "@types/json-schema@^7.0.6": - version "7.0.7" - resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad" - integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA== - -"@types/minimatch@*": - version "3.0.3" - resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d" - integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== - -"@types/node@*": - version "14.14.34" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.34.tgz#07935194fc049069a1c56c0c274265abeddf88da" - integrity sha512-dBPaxocOK6UVyvhbnpFIj2W+S+1cBTkHQbFQfeeJhoKFbzYcVUGHvddeWPSucKATb3F0+pgDq0i6ghEaZjsugA== - -"@types/node@12.12.21": - version "12.12.21" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.21.tgz#aa44a6363291c7037111c47e4661ad210aded23f" - integrity sha512-8sRGhbpU+ck1n0PGAUgVrWrWdjSW2aqNeyC15W88GRsMpSwzv6RJGlLhE7s2RhVSOdyDmxbqlWSeThq4/7xqlA== - -"@types/node@^12.0.12": - version "12.20.5" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.5.tgz#4ca82a766f05c359fd6c77505007e5a272f4bb9b" - integrity sha512-5Oy7tYZnu3a4pnJ//d4yVvOImExl4Vtwf0D40iKUlU+XlUsyV9iyFWyCFlwy489b72FMAik/EFwRkNLjjOdSPg== - -"@types/parse-json@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" - integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA== - -"@types/q@^0.0.32": - version "0.0.32" - resolved "https://registry.yarnpkg.com/@types/q/-/q-0.0.32.tgz#bd284e57c84f1325da702babfc82a5328190c0c5" - integrity sha1-vShOV8hPEyXacCur/IKlMoGQwMU= - -"@types/q@^1.5.1": - version "1.5.4" - resolved "https://registry.yarnpkg.com/@types/q/-/q-1.5.4.tgz#15925414e0ad2cd765bfef58842f7e26a7accb24" - integrity sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug== - -"@types/selenium-webdriver@^3.0.0": - version "3.0.17" - resolved "https://registry.yarnpkg.com/@types/selenium-webdriver/-/selenium-webdriver-3.0.17.tgz#50bea0c3c2acc31c959c5b1e747798b3b3d06d4b" - integrity sha512-tGomyEuzSC1H28y2zlW6XPCaDaXFaD6soTdb4GNdmte2qfHtrKqhy0ZFs4r/1hpazCfEZqeTSRLvSasmEx89uw== - -"@types/source-list-map@*": - version "0.1.2" - resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9" - integrity sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA== - -"@types/webpack-sources@^0.1.5": - version "0.1.8" - resolved "https://registry.yarnpkg.com/@types/webpack-sources/-/webpack-sources-0.1.8.tgz#078d75410435993ec8a0a2855e88706f3f751f81" - integrity sha512-JHB2/xZlXOjzjBB6fMOpH1eQAfsrpqVVIbneE0Rok16WXwFaznaI5vfg75U5WgGJm7V9W1c4xeRQDjX/zwvghA== - dependencies: - "@types/node" "*" - "@types/source-list-map" "*" - source-map "^0.6.1" - -"@webassemblyjs/ast@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.9.0.tgz#bd850604b4042459a5a41cd7d338cbed695ed964" - integrity sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA== - dependencies: - "@webassemblyjs/helper-module-context" "1.9.0" - "@webassemblyjs/helper-wasm-bytecode" "1.9.0" - "@webassemblyjs/wast-parser" "1.9.0" - -"@webassemblyjs/floating-point-hex-parser@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.9.0.tgz#3c3d3b271bddfc84deb00f71344438311d52ffb4" - integrity sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA== - -"@webassemblyjs/helper-api-error@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.9.0.tgz#203f676e333b96c9da2eeab3ccef33c45928b6a2" - integrity sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw== - -"@webassemblyjs/helper-buffer@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.9.0.tgz#a1442d269c5feb23fcbc9ef759dac3547f29de00" - integrity sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA== - -"@webassemblyjs/helper-code-frame@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-code-frame/-/helper-code-frame-1.9.0.tgz#647f8892cd2043a82ac0c8c5e75c36f1d9159f27" - integrity sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA== - dependencies: - "@webassemblyjs/wast-printer" "1.9.0" - -"@webassemblyjs/helper-fsm@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-fsm/-/helper-fsm-1.9.0.tgz#c05256b71244214671f4b08ec108ad63b70eddb8" - integrity sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw== - -"@webassemblyjs/helper-module-context@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-module-context/-/helper-module-context-1.9.0.tgz#25d8884b76839871a08a6c6f806c3979ef712f07" - integrity sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g== - dependencies: - "@webassemblyjs/ast" "1.9.0" - -"@webassemblyjs/helper-wasm-bytecode@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.9.0.tgz#4fed8beac9b8c14f8c58b70d124d549dd1fe5790" - integrity sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw== - -"@webassemblyjs/helper-wasm-section@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.9.0.tgz#5a4138d5a6292ba18b04c5ae49717e4167965346" - integrity sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-buffer" "1.9.0" - "@webassemblyjs/helper-wasm-bytecode" "1.9.0" - "@webassemblyjs/wasm-gen" "1.9.0" - -"@webassemblyjs/ieee754@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.9.0.tgz#15c7a0fbaae83fb26143bbacf6d6df1702ad39e4" - integrity sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg== - dependencies: - "@xtuc/ieee754" "^1.2.0" - -"@webassemblyjs/leb128@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.9.0.tgz#f19ca0b76a6dc55623a09cffa769e838fa1e1c95" - integrity sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw== - dependencies: - "@xtuc/long" "4.2.2" - -"@webassemblyjs/utf8@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.9.0.tgz#04d33b636f78e6a6813227e82402f7637b6229ab" - integrity sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w== - -"@webassemblyjs/wasm-edit@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.9.0.tgz#3fe6d79d3f0f922183aa86002c42dd256cfee9cf" - integrity sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-buffer" "1.9.0" - "@webassemblyjs/helper-wasm-bytecode" "1.9.0" - "@webassemblyjs/helper-wasm-section" "1.9.0" - "@webassemblyjs/wasm-gen" "1.9.0" - "@webassemblyjs/wasm-opt" "1.9.0" - "@webassemblyjs/wasm-parser" "1.9.0" - "@webassemblyjs/wast-printer" "1.9.0" - -"@webassemblyjs/wasm-gen@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.9.0.tgz#50bc70ec68ded8e2763b01a1418bf43491a7a49c" - integrity sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-wasm-bytecode" "1.9.0" - "@webassemblyjs/ieee754" "1.9.0" - "@webassemblyjs/leb128" "1.9.0" - "@webassemblyjs/utf8" "1.9.0" - -"@webassemblyjs/wasm-opt@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.9.0.tgz#2211181e5b31326443cc8112eb9f0b9028721a61" - integrity sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-buffer" "1.9.0" - "@webassemblyjs/wasm-gen" "1.9.0" - "@webassemblyjs/wasm-parser" "1.9.0" - -"@webassemblyjs/wasm-parser@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.9.0.tgz#9d48e44826df4a6598294aa6c87469d642fff65e" - integrity sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-api-error" "1.9.0" - "@webassemblyjs/helper-wasm-bytecode" "1.9.0" - "@webassemblyjs/ieee754" "1.9.0" - "@webassemblyjs/leb128" "1.9.0" - "@webassemblyjs/utf8" "1.9.0" - -"@webassemblyjs/wast-parser@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-parser/-/wast-parser-1.9.0.tgz#3031115d79ac5bd261556cecc3fa90a3ef451914" - integrity sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/floating-point-hex-parser" "1.9.0" - "@webassemblyjs/helper-api-error" "1.9.0" - "@webassemblyjs/helper-code-frame" "1.9.0" - "@webassemblyjs/helper-fsm" "1.9.0" - "@xtuc/long" "4.2.2" - -"@webassemblyjs/wast-printer@1.9.0": - version "1.9.0" - resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.9.0.tgz#4935d54c85fef637b00ce9f52377451d00d47899" - integrity sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/wast-parser" "1.9.0" - "@xtuc/long" "4.2.2" - -"@xtuc/ieee754@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" - integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== - -"@xtuc/long@4.2.2": - version "4.2.2" - resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" - integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== - -"@yarnpkg/lockfile@1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31" - integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ== - -JSONStream@^1.3.4: - version "1.3.5" - resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" - integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== - dependencies: - jsonparse "^1.2.0" - through ">=2.2.7 <3" - -abab@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" - integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== - -accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.7: - version "1.3.7" - resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.7.tgz#531bc726517a3b2b41f850021c6cc15eaab507cd" - integrity sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA== - dependencies: - mime-types "~2.1.24" - negotiator "0.6.2" - -acorn@^6.4.1: - version "6.4.2" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.2.tgz#35866fd710528e92de10cf06016498e47e39e1e6" - integrity sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ== - -adjust-sourcemap-loader@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/adjust-sourcemap-loader/-/adjust-sourcemap-loader-3.0.0.tgz#5ae12fb5b7b1c585e80bbb5a63ec163a1a45e61e" - integrity sha512-YBrGyT2/uVQ/c6Rr+t6ZJXniY03YtHGMJQYal368burRGYKqhx9qGTWqcBU5s1CwYY9E/ri63RYyG1IacMZtqw== - dependencies: - loader-utils "^2.0.0" - regex-parser "^2.2.11" - -adm-zip@^0.4.9: - version "0.4.16" - resolved "https://registry.yarnpkg.com/adm-zip/-/adm-zip-0.4.16.tgz#cf4c508fdffab02c269cbc7f471a875f05570365" - integrity sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg== - -after@0.8.2: - version "0.8.2" - resolved "https://registry.yarnpkg.com/after/-/after-0.8.2.tgz#fedb394f9f0e02aa9768e702bda23b505fae7e1f" - integrity sha1-/ts5T58OAqqXaOcCvaI7UF+ufh8= - -ag-grid-angular@24.1.0: - version "24.1.0" - resolved "https://registry.yarnpkg.com/ag-grid-angular/-/ag-grid-angular-24.1.0.tgz#98c238003bf0dd46d43a2fd7d2f1aec1eeeafbac" - integrity sha512-rCcRWq6iC8yvRBk99qANSiiZGsUDo9E3PcFVDd7Xs2thRuJ/yrVhCSZEkCG/SlXHX2ZaMxzIxI+2ChPV2MJSEQ== - dependencies: - tslib "^1.10.0" - -ag-grid-community@23.2.1: - version "23.2.1" - resolved "https://registry.yarnpkg.com/ag-grid-community/-/ag-grid-community-23.2.1.tgz#0cf2beb7ba2fcf66a4865e45faabdbea173d59b6" - integrity sha512-WHfDV7mFsd6eQJ5yOu26nnpnW7Gd02zcp9LHHfKQky8lfgTEo382qcja7MibHzB2f5vOCBdQnOPsWrbBNKRzhQ== - -agent-base@4, agent-base@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.3.0.tgz#8165f01c436009bccad0b1d122f05ed770efc6ee" - integrity sha512-salcGninV0nPrwpGNn4VTXBb1SOuXQBiqbrNXoeizJsHrsL6ERFM2Ne3JUSBWRE6aeNJI2ROP/WEEIDUiDe3cg== - dependencies: - es6-promisify "^5.0.0" - -agent-base@~4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9" - integrity sha512-JVwXMr9nHYTUXsBFKUqhJwvlcYU/blreOEUkhNR2eXZIvwd+c+o5V4MgDPKWnMS/56awN3TRzIP+KoPn+roQtg== - dependencies: - es6-promisify "^5.0.0" - -agentkeepalive@^3.4.1: - version "3.5.2" - resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-3.5.2.tgz#a113924dd3fa24a0bc3b78108c450c2abee00f67" - integrity sha512-e0L/HNe6qkQ7H19kTlRRqUibEAwDK5AFk6y3PtMsuut2VAH6+Q4xZml1tNDJD7kSAyqmbG/K08K5WEJYtUrSlQ== - dependencies: - humanize-ms "^1.2.1" - -aggregate-error@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a" - integrity sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA== - dependencies: - clean-stack "^2.0.0" - indent-string "^4.0.0" - -ajv-errors@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d" - integrity sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ== - -ajv-keywords@^3.1.0, ajv-keywords@^3.4.1, ajv-keywords@^3.5.2: - version "3.5.2" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" - integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== - -ajv@6.12.6, ajv@^6.1.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4, ajv@^6.12.5: - version "6.12.6" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" - integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== - 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" - -alphanum-sort@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" - integrity sha1-l6ERlkmyEa0zaR2fn0hqjsn74KM= - -angular2-uuid@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/angular2-uuid/-/angular2-uuid-1.1.1.tgz#72f03cd532b7f40032eb1ecfb9f8457384be956e" - integrity sha1-cvA81TK39AAy6x7PufhFc4S+lW4= - -ansi-colors@4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" - integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== - -ansi-colors@^3.0.0: - version "3.2.4" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf" - integrity sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA== - -ansi-escapes@^4.2.1: - version "4.3.1" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.1.tgz#a5c47cc43181f1f38ffd7076837700d395522a61" - integrity sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA== - dependencies: - type-fest "^0.11.0" - -ansi-html@0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.7.tgz#813584021962a9e9e6fd039f940d12f56ca7859e" - integrity sha1-gTWEAhliqenm/QOflA0S9WynhZ4= - -ansi-regex@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" - integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8= - -ansi-regex@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" - integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== - -ansi-regex@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" - integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== - -ansi-styles@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" - integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= - -ansi-styles@^3.2.0, ansi-styles@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" - integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== - dependencies: - color-convert "^1.9.0" - -ansi-styles@^4.0.0, ansi-styles@^4.1.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" - integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== - dependencies: - color-convert "^2.0.1" - -anymatch@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-2.0.0.tgz#bcb24b4f37934d9aa7ac17b4adaf89e7c76ef2eb" - integrity sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw== - dependencies: - micromatch "^3.1.4" - normalize-path "^2.1.1" - -anymatch@~3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" - integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg== - dependencies: - normalize-path "^3.0.0" - picomatch "^2.0.4" - -app-root-path@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/app-root-path/-/app-root-path-3.0.0.tgz#210b6f43873227e18a4b810a032283311555d5ad" - integrity sha512-qMcx+Gy2UZynHjOHOIXPNvpf+9cjvk3cWrBBK7zg4gH9+clobJRb9NGzcT7mQTcV/6Gm/1WelUtqxVXnNlrwcw== - -aproba@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" - integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== - -arg@^4.1.0: - version "4.1.3" - resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" - integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== - -argparse@^1.0.7: - version "1.0.10" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" - integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== - dependencies: - sprintf-js "~1.0.2" - -aria-query@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-3.0.0.tgz#65b3fcc1ca1155a8c9ae64d6eee297f15d5133cc" - integrity sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w= - dependencies: - ast-types-flow "0.0.7" - commander "^2.11.0" - -arity-n@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/arity-n/-/arity-n-1.0.4.tgz#d9e76b11733e08569c0847ae7b39b2860b30b745" - integrity sha1-2edrEXM+CFacCEeuezmyhgswt0U= - -arr-diff@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" - integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= - -arr-flatten@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" - integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== - -arr-union@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" - integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= - -array-flatten@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2" - integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI= - -array-flatten@^2.1.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-2.1.2.tgz#24ef80a28c1a893617e2149b0c6d0d788293b099" - integrity sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ== - -array-union@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" - integrity sha1-mjRBDk9OPaI96jdb5b5w8kd47Dk= - dependencies: - array-uniq "^1.0.1" - -array-union@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" - integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== - -array-uniq@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/array-uniq/-/array-uniq-1.0.3.tgz#af6ac877a25cc7f74e058894753858dfdb24fdb6" - integrity sha1-r2rId6Jcx/dOBYiUdThY39sk/bY= - -array-unique@^0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" - integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= - -arraybuffer.slice@~0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz#3bbc4275dd584cc1b10809b89d4e8b63a69e7675" - integrity sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog== - -arrify@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" - integrity sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0= - -asn1.js@^5.2.0: - version "5.4.1" - resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07" - integrity sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA== - dependencies: - bn.js "^4.0.0" - inherits "^2.0.1" - minimalistic-assert "^1.0.0" - safer-buffer "^2.1.0" - -asn1@~0.2.3: - version "0.2.4" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" - integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== - dependencies: - safer-buffer "~2.1.0" - -assert-plus@1.0.0, assert-plus@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" - integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= - -assert@^1.1.1: - version "1.5.0" - resolved "https://registry.yarnpkg.com/assert/-/assert-1.5.0.tgz#55c109aaf6e0aefdb3dc4b71240c70bf574b18eb" - integrity sha512-EDsgawzwoun2CZkCgtxJbv392v4nbk9XDD06zI+kQYoBM/3RBWLlEyJARDOmhAAosBjWACEkKL6S+lIZtcAubA== - dependencies: - object-assign "^4.1.1" - util "0.10.3" - -assign-symbols@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" - integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= - -ast-types-flow@0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad" - integrity sha1-9wtzXGvKGlycItmCw+Oef+ujva0= - -async-each@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" - integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== - -async-limiter@~1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd" - integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ== - -async@^2.6.2: - version "2.6.3" - resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff" - integrity sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg== - dependencies: - lodash "^4.17.14" - -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= - -atob@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" - integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== - -autoprefixer@9.8.6: - version "9.8.6" - resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.8.6.tgz#3b73594ca1bf9266320c5acf1588d74dea74210f" - integrity sha512-XrvP4VVHdRBCdX1S3WXVD8+RyG9qeb1D5Sn1DeLiG2xfSpzellk5k54xbUERJ3M5DggQxes39UGOTP8CFrEGbg== - dependencies: - browserslist "^4.12.0" - caniuse-lite "^1.0.30001109" - colorette "^1.2.1" - normalize-range "^0.1.2" - num2fraction "^1.2.2" - postcss "^7.0.32" - postcss-value-parser "^4.1.0" - -aws-sign2@~0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" - integrity sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg= - -aws4@^1.8.0: - version "1.11.0" - resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.11.0.tgz#d61f46d83b2519250e2784daf5b09479a8b41c59" - integrity sha512-xh1Rl34h6Fi1DC2WWKfxUTVqRsNnr6LsKz2+hfwDxQJWmrx8+c7ylaqBMcHfl1U1r2dsifOvKX3LQuLNZ+XSvA== - -axobject-query@2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.0.2.tgz#ea187abe5b9002b377f925d8bf7d1c561adf38f9" - integrity sha512-MCeek8ZH7hKyO1rWUbKNQBbl4l2eY0ntk7OGi+q0RlafrCnfPxC06WZA+uebCfmYp4mNU9jRBP1AhGyf8+W3ww== - dependencies: - ast-types-flow "0.0.7" - -babel-loader@8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/babel-loader/-/babel-loader-8.1.0.tgz#c611d5112bd5209abe8b9fa84c3e4da25275f1c3" - integrity sha512-7q7nC1tYOrqvUrN3LQK4GwSk/TQorZSOlO9C+RZDZpODgyN4ZlCqE5q9cDsyWOliN+aU9B4JX01xK9eJXowJLw== - dependencies: - find-cache-dir "^2.1.0" - loader-utils "^1.4.0" - mkdirp "^0.5.3" - pify "^4.0.1" - schema-utils "^2.6.5" - -babel-plugin-dynamic-import-node@^2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/babel-plugin-dynamic-import-node/-/babel-plugin-dynamic-import-node-2.3.3.tgz#84fda19c976ec5c6defef57f9427b3def66e17a3" - integrity sha512-jZVI+s9Zg3IqA/kdi0i6UDCybUI3aSBLnglhYbSSjKlV7yF1F/5LWv8MakQmvYpnbJDS6fcBL2KzHSxNCMtWSQ== - dependencies: - object.assign "^4.1.0" - -backo2@1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947" - integrity sha1-MasayLEpNjRj41s+u2n038+6eUc= - -balanced-match@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" - integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= - -base64-arraybuffer@0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-0.1.4.tgz#9818c79e059b1355f97e0428a017c838e90ba812" - integrity sha1-mBjHngWbE1X5fgQooBfIOOkLqBI= - -base64-js@^1.0.2: - version "1.5.1" - resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" - integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== - -base64id@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/base64id/-/base64id-2.0.0.tgz#2770ac6bc47d312af97a8bf9a634342e0cd25cb6" - integrity sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog== - -base@^0.11.1: - version "0.11.2" - resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" - integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== - dependencies: - cache-base "^1.0.1" - class-utils "^0.3.5" - component-emitter "^1.2.1" - define-property "^1.0.0" - isobject "^3.0.1" - mixin-deep "^1.2.0" - pascalcase "^0.1.1" - -batch@0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/batch/-/batch-0.6.1.tgz#dc34314f4e679318093fc760272525f94bf25c16" - integrity sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY= - -bcrypt-pbkdf@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" - integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= - dependencies: - tweetnacl "^0.14.3" - -big.js@^5.2.2: - version "5.2.2" - resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" - integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== - -binary-extensions@^1.0.0: - version "1.13.1" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" - integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== - -binary-extensions@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" - integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== - -bindings@^1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" - integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== - dependencies: - file-uri-to-path "1.0.0" - -blob@0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/blob/-/blob-0.0.5.tgz#d680eeef25f8cd91ad533f5b01eed48e64caf683" - integrity sha512-gaqbzQPqOoamawKg0LGVd7SzLgXS+JH61oWprSLH+P+abTczqJbhTR8CmJ2u9/bUYNmHTGJx/UEmn6doAvvuig== - -blocking-proxy@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/blocking-proxy/-/blocking-proxy-1.0.1.tgz#81d6fd1fe13a4c0d6957df7f91b75e98dac40cb2" - integrity sha512-KE8NFMZr3mN2E0HcvCgRtX7DjhiIQrwle+nSVJVC/yqFb9+xznHl2ZcoBp2L9qzkI4t4cBFJ1efXF8Dwi132RA== - dependencies: - minimist "^1.2.0" - -bluebird@^3.5.1, bluebird@^3.5.3, bluebird@^3.5.5: - version "3.7.2" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" - integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== - -bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.9: - version "4.12.0" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" - integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== - -bn.js@^5.0.0, bn.js@^5.1.1: - version "5.2.0" - resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.2.0.tgz#358860674396c6997771a9d051fcc1b57d4ae002" - integrity sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw== - -body-parser@1.19.0, body-parser@^1.19.0: - version "1.19.0" - resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.0.tgz#96b2709e57c9c4e09a6fd66a8fd979844f69f08a" - integrity sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw== - dependencies: - bytes "3.1.0" - content-type "~1.0.4" - debug "2.6.9" - depd "~1.1.2" - http-errors "1.7.2" - iconv-lite "0.4.24" - on-finished "~2.3.0" - qs "6.7.0" - raw-body "2.4.0" - type-is "~1.6.17" - -bonjour@^3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/bonjour/-/bonjour-3.5.0.tgz#8e890a183d8ee9a2393b3844c691a42bcf7bc9f5" - integrity sha1-jokKGD2O6aI5OzhExpGkK897yfU= - dependencies: - array-flatten "^2.1.0" - deep-equal "^1.0.1" - dns-equal "^1.0.0" - dns-txt "^2.0.2" - multicast-dns "^6.0.1" - multicast-dns-service-types "^1.1.0" - -boolbase@^1.0.0, boolbase@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" - integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= - -boolean@^3.0.1: - version "3.0.2" - resolved "https://registry.yarnpkg.com/boolean/-/boolean-3.0.2.tgz#df1baa18b6a2b0e70840475e1d93ec8fe75b2570" - integrity sha512-RwywHlpCRc3/Wh81MiCKun4ydaIFyW5Ea6JbL6sRCVx5q5irDw7pMXBUFYF/jArQ6YrG36q0kpovc9P/Kd3I4g== - -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -braces@^2.3.1, braces@^2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" - integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== - dependencies: - arr-flatten "^1.1.0" - array-unique "^0.3.2" - extend-shallow "^2.0.1" - fill-range "^4.0.0" - isobject "^3.0.1" - repeat-element "^1.1.2" - snapdragon "^0.8.1" - snapdragon-node "^2.0.1" - split-string "^3.0.2" - to-regex "^3.0.1" - -braces@^3.0.1, braces@^3.0.2, braces@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" - integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== - dependencies: - fill-range "^7.0.1" - -brorand@^1.0.1, brorand@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" - integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= - -browserify-aes@^1.0.0, browserify-aes@^1.0.4: - version "1.2.0" - resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48" - integrity sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA== - dependencies: - buffer-xor "^1.0.3" - cipher-base "^1.0.0" - create-hash "^1.1.0" - evp_bytestokey "^1.0.3" - inherits "^2.0.1" - safe-buffer "^5.0.1" - -browserify-cipher@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/browserify-cipher/-/browserify-cipher-1.0.1.tgz#8d6474c1b870bfdabcd3bcfcc1934a10e94f15f0" - integrity sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w== - dependencies: - browserify-aes "^1.0.4" - browserify-des "^1.0.0" - evp_bytestokey "^1.0.0" - -browserify-des@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/browserify-des/-/browserify-des-1.0.2.tgz#3af4f1f59839403572f1c66204375f7a7f703e9c" - integrity sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A== - dependencies: - cipher-base "^1.0.1" - des.js "^1.0.0" - inherits "^2.0.1" - safe-buffer "^5.1.2" - -browserify-rsa@^4.0.0, browserify-rsa@^4.0.1: - version "4.1.0" - resolved "https://registry.yarnpkg.com/browserify-rsa/-/browserify-rsa-4.1.0.tgz#b2fd06b5b75ae297f7ce2dc651f918f5be158c8d" - integrity sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog== - dependencies: - bn.js "^5.0.0" - randombytes "^2.0.1" - -browserify-sign@^4.0.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/browserify-sign/-/browserify-sign-4.2.1.tgz#eaf4add46dd54be3bb3b36c0cf15abbeba7956c3" - integrity sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg== - dependencies: - bn.js "^5.1.1" - browserify-rsa "^4.0.1" - create-hash "^1.2.0" - create-hmac "^1.1.7" - elliptic "^6.5.3" - inherits "^2.0.4" - parse-asn1 "^5.1.5" - readable-stream "^3.6.0" - safe-buffer "^5.2.0" - -browserify-zlib@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/browserify-zlib/-/browserify-zlib-0.2.0.tgz#2869459d9aa3be245fe8fe2ca1f46e2e7f54d73f" - integrity sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA== - dependencies: - pako "~1.0.5" - -browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.14.5, browserslist@^4.16.3, browserslist@^4.9.1: - version "4.16.3" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.16.3.tgz#340aa46940d7db878748567c5dea24a48ddf3717" - integrity sha512-vIyhWmIkULaq04Gt93txdh+j02yX/JzlyhLYbV3YQCn/zvES3JnY7TifHHvvr1w5hTDluNKMkV05cs4vy8Q7sw== - dependencies: - caniuse-lite "^1.0.30001181" - colorette "^1.2.1" - electron-to-chromium "^1.3.649" - escalade "^3.1.1" - node-releases "^1.1.70" - -browserstack@^1.5.1: - version "1.6.1" - resolved "https://registry.yarnpkg.com/browserstack/-/browserstack-1.6.1.tgz#e051f9733ec3b507659f395c7a4765a1b1e358b3" - integrity sha512-GxtFjpIaKdbAyzHfFDKixKO8IBT7wR3NjbzrGc78nNs/Ciys9wU3/nBtsqsWv5nDSrdI5tz0peKuzCPuNXNUiw== - dependencies: - https-proxy-agent "^2.2.1" - -buffer-crc32@~0.2.3: - version "0.2.13" - resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" - integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= - -buffer-from@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" - integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== - -buffer-indexof@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/buffer-indexof/-/buffer-indexof-1.1.1.tgz#52fabcc6a606d1a00302802648ef68f639da268c" - integrity sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g== - -buffer-xor@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" - integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= - -buffer@^4.3.0: - version "4.9.2" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8" - integrity sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg== - dependencies: - base64-js "^1.0.2" - ieee754 "^1.1.4" - isarray "^1.0.0" - -builtin-modules@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-1.1.1.tgz#270f076c5a72c02f5b65a47df94c5fe3a278892f" - integrity sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8= - -builtin-status-codes@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8" - integrity sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug= - -builtins@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/builtins/-/builtins-1.0.3.tgz#cb94faeb61c8696451db36534e1422f94f0aee88" - integrity sha1-y5T662HIaWRR2zZTThQi+U8K7og= - -bytes@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" - integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg= - -bytes@3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6" - integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg== - -cacache@15.0.5, cacache@^15.0.5: - version "15.0.5" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-15.0.5.tgz#69162833da29170d6732334643c60e005f5f17d0" - integrity sha512-lloiL22n7sOjEEXdL8NAjTgv9a1u43xICE9/203qonkZUCj5X1UEWIdf2/Y0d6QcCtMzbKQyhrcDbdvlZTs/+A== - dependencies: - "@npmcli/move-file" "^1.0.1" - chownr "^2.0.0" - fs-minipass "^2.0.0" - glob "^7.1.4" - infer-owner "^1.0.4" - lru-cache "^6.0.0" - minipass "^3.1.1" - minipass-collect "^1.0.2" - minipass-flush "^1.0.5" - minipass-pipeline "^1.2.2" - mkdirp "^1.0.3" - p-map "^4.0.0" - promise-inflight "^1.0.1" - rimraf "^3.0.2" - ssri "^8.0.0" - tar "^6.0.2" - unique-filename "^1.1.1" - -cacache@^12.0.0, cacache@^12.0.2: - version "12.0.4" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-12.0.4.tgz#668bcbd105aeb5f1d92fe25570ec9525c8faa40c" - integrity sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ== - dependencies: - bluebird "^3.5.5" - chownr "^1.1.1" - figgy-pudding "^3.5.1" - glob "^7.1.4" - graceful-fs "^4.1.15" - infer-owner "^1.0.3" - lru-cache "^5.1.1" - mississippi "^3.0.0" - mkdirp "^0.5.1" - move-concurrently "^1.0.1" - promise-inflight "^1.0.1" - rimraf "^2.6.3" - ssri "^6.0.1" - unique-filename "^1.1.1" - y18n "^4.0.0" - -cache-base@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" - integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== - dependencies: - collection-visit "^1.0.0" - component-emitter "^1.2.1" - get-value "^2.0.6" - has-value "^1.0.0" - isobject "^3.0.1" - set-value "^2.0.0" - to-object-path "^0.3.0" - union-value "^1.0.0" - unset-value "^1.0.0" - -cacheable-request@^6.0.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz#20ffb8bd162ba4be11e9567d823db651052ca912" - integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg== - dependencies: - clone-response "^1.0.2" - get-stream "^5.1.0" - http-cache-semantics "^4.0.0" - keyv "^3.0.0" - lowercase-keys "^2.0.0" - normalize-url "^4.1.0" - responselike "^1.0.2" - -call-bind@^1.0.0, call-bind@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" - integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== - dependencies: - function-bind "^1.1.1" - get-intrinsic "^1.0.2" - -caller-callsite@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/caller-callsite/-/caller-callsite-2.0.0.tgz#847e0fce0a223750a9a027c54b33731ad3154134" - integrity sha1-hH4PzgoiN1CpoCfFSzNzGtMVQTQ= - dependencies: - callsites "^2.0.0" - -caller-path@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/caller-path/-/caller-path-2.0.0.tgz#468f83044e369ab2010fac5f06ceee15bb2cb1f4" - integrity sha1-Ro+DBE42mrIBD6xfBs7uFbsssfQ= - dependencies: - caller-callsite "^2.0.0" - -callsites@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" - integrity sha1-BuuE8A7qQT2oav/vrL/7Ngk7PFA= - -callsites@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" - integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== - -camelcase@5.3.1, camelcase@^5.0.0: - version "5.3.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" - integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== - -camelcase@^6.0.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.2.0.tgz#924af881c9d525ac9d87f40d964e5cea982a1809" - integrity sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg== - -caniuse-api@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/caniuse-api/-/caniuse-api-3.0.0.tgz#5e4d90e2274961d46291997df599e3ed008ee4c0" - integrity sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw== - dependencies: - browserslist "^4.0.0" - caniuse-lite "^1.0.0" - lodash.memoize "^4.1.2" - lodash.uniq "^4.5.0" - -caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001032, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001181: - version "1.0.30001199" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001199.tgz#062afccaad21023e2e647d767bac4274b8b8fd7f" - integrity sha512-ifbK2eChUCFUwGhlEzIoVwzFt1+iriSjyKKFYNfv6hN34483wyWpLLavYQXhnR036LhkdUYaSDpHg1El++VgHQ== - -canonical-path@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/canonical-path/-/canonical-path-1.0.0.tgz#fcb470c23958def85081856be7a86e904f180d1d" - integrity sha512-feylzsbDxi1gPZ1IjystzIQZagYYLvfKrSuygUCgf7z6x790VEzze5QEkdSV1U58RA7Hi0+v6fv4K54atOzATg== - -caseless@~0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" - integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= - -chalk@^1.1.1, chalk@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" - integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= - dependencies: - ansi-styles "^2.2.1" - escape-string-regexp "^1.0.2" - has-ansi "^2.0.0" - strip-ansi "^3.0.0" - supports-color "^2.0.0" - -chalk@^2.0.0, chalk@^2.0.1, chalk@^2.3.0, chalk@^2.4.1, chalk@^2.4.2: - version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" - 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" - -chalk@^4.0.0, chalk@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a" - integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -chardet@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" - integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== - -"chokidar@>=2.0.0 <4.0.0", chokidar@^3.0.0, chokidar@^3.4.1: - version "3.5.1" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.1.tgz#ee9ce7bbebd2b79f49f304799d5468e31e14e68a" - integrity sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw== - dependencies: - anymatch "~3.1.1" - braces "~3.0.2" - glob-parent "~5.1.0" - is-binary-path "~2.1.0" - is-glob "~4.0.1" - normalize-path "~3.0.0" - readdirp "~3.5.0" - optionalDependencies: - fsevents "~2.3.1" - -chokidar@^2.1.8: - version "2.1.8" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" - integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg== - dependencies: - anymatch "^2.0.0" - async-each "^1.0.1" - braces "^2.3.2" - glob-parent "^3.1.0" - inherits "^2.0.3" - is-binary-path "^1.0.0" - is-glob "^4.0.0" - normalize-path "^3.0.0" - path-is-absolute "^1.0.0" - readdirp "^2.2.1" - upath "^1.1.1" - optionalDependencies: - fsevents "^1.2.7" - -chownr@^1.1.1, chownr@^1.1.2: - version "1.1.4" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" - integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== - -chownr@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece" - integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ== - -chrome-trace-event@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.2.tgz#234090ee97c7d4ad1a2c4beae27505deffc608a4" - integrity sha512-9e/zx1jw7B4CO+c/RXoCsfg/x1AfUBioy4owYH0bJprEYAx5hRFLRhWBqHAG57D0ZM4H7vxbP7bPe0VwhQRYDQ== - dependencies: - tslib "^1.9.0" - -cipher-base@^1.0.0, cipher-base@^1.0.1, cipher-base@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/cipher-base/-/cipher-base-1.0.4.tgz#8760e4ecc272f4c363532f926d874aae2c1397de" - integrity sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q== - dependencies: - inherits "^2.0.1" - safe-buffer "^5.0.1" - -circular-dependency-plugin@5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/circular-dependency-plugin/-/circular-dependency-plugin-5.2.0.tgz#e09dbc2dd3e2928442403e2d45b41cea06bc0a93" - integrity sha512-7p4Kn/gffhQaavNfyDFg7LS5S/UT1JAjyGd4UqR2+jzoYF02eDkj0Ec3+48TsIa4zghjLY87nQHIh/ecK9qLdw== - -class-utils@^0.3.5: - version "0.3.6" - resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" - integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== - dependencies: - arr-union "^3.1.0" - define-property "^0.2.5" - isobject "^3.0.0" - static-extend "^0.1.1" - -clean-stack@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" - integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== - -cli-cursor@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" - integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== - dependencies: - restore-cursor "^3.1.0" - -cli-spinners@^2.4.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.5.0.tgz#12763e47251bf951cb75c201dfa58ff1bcb2d047" - integrity sha512-PC+AmIuK04E6aeSs/pUccSujsTzBhu4HzC2dL+CfJB/Jcc2qTRbEwZQDfIUpt2Xl8BodYBEq8w4fc0kU2I9DjQ== - -cli-width@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" - integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== - -cliui@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" - integrity sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA== - dependencies: - string-width "^3.1.0" - strip-ansi "^5.2.0" - wrap-ansi "^5.1.0" - -cliui@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" - integrity sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.0" - wrap-ansi "^6.2.0" - -cliui@^7.0.2: - version "7.0.4" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" - integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.0" - wrap-ansi "^7.0.0" - -clone-deep@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" - integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== - dependencies: - is-plain-object "^2.0.4" - kind-of "^6.0.2" - shallow-clone "^3.0.0" - -clone-response@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b" - integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= - dependencies: - mimic-response "^1.0.0" - -clone@^1.0.2: - version "1.0.4" - resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" - integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4= - -coa@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/coa/-/coa-2.0.2.tgz#43f6c21151b4ef2bf57187db0d73de229e3e7ec3" - integrity sha512-q5/jG+YQnSy4nRTV4F7lPepBJZ8qBNJJDBuJdoejDyLXgmL7IEo+Le2JDZudFTFt7mrCqIRaSjws4ygRCTCAXA== - dependencies: - "@types/q" "^1.5.1" - chalk "^2.4.1" - q "^1.1.2" - -codelyzer@6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/codelyzer/-/codelyzer-6.0.0.tgz#50c98581cc2890e0e9a9f93878dc317115d836ed" - integrity sha512-edJIQCIcxD9DhVSyBEdJ38AbLikm515Wl91t5RDGNT88uA6uQdTm4phTWfn9JhzAI8kXNUcfYyAE90lJElpGtA== - dependencies: - "@angular/compiler" "9.0.0" - "@angular/core" "9.0.0" - app-root-path "^3.0.0" - aria-query "^3.0.0" - axobject-query "2.0.2" - css-selector-tokenizer "^0.7.1" - cssauron "^1.4.0" - damerau-levenshtein "^1.0.4" - rxjs "^6.5.3" - semver-dsl "^1.0.1" - source-map "^0.5.7" - sprintf-js "^1.1.2" - tslib "^1.10.0" - zone.js "~0.10.3" - -collection-visit@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" - integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= - dependencies: - map-visit "^1.0.0" - object-visit "^1.0.0" - -color-convert@^1.9.0, color-convert@^1.9.1: - version "1.9.3" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" - integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== - dependencies: - color-name "1.1.3" - -color-convert@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== - dependencies: - color-name "~1.1.4" - -color-name@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= - -color-name@^1.0.0, color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - -color-string@^1.5.4: - version "1.5.5" - resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.5.5.tgz#65474a8f0e7439625f3d27a6a19d89fc45223014" - integrity sha512-jgIoum0OfQfq9Whcfc2z/VhCNcmQjWbey6qBX0vqt7YICflUmBCh9E9CiQD5GSJ+Uehixm3NUwHVhqUAWRivZg== - dependencies: - color-name "^1.0.0" - simple-swizzle "^0.2.2" - -color@^3.0.0: - version "3.1.3" - resolved "https://registry.yarnpkg.com/color/-/color-3.1.3.tgz#ca67fb4e7b97d611dcde39eceed422067d91596e" - integrity sha512-xgXAcTHa2HeFCGLE9Xs/R82hujGtu9Jd9x4NW3T34+OMs7VoPsjwzRczKHvTAHeJwWFwX5j15+MgAppE8ztObQ== - dependencies: - color-convert "^1.9.1" - color-string "^1.5.4" - -colorette@^1.2.1: - version "1.2.2" - resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.2.2.tgz#cbcc79d5e99caea2dbf10eb3a26fd8b3e6acfa94" - integrity sha512-MKGMzyfeuutC/ZJ1cba9NqcNpfeqMUcYmyF1ZFY6/Cn7CNSAKx6a+s48sqLqyAiZuaP2TcqMhoo+dlwFnVxT9w== - -colors@1.4.0, colors@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" - integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== - -combined-stream@^1.0.6, combined-stream@~1.0.6: - version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" - integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== - dependencies: - delayed-stream "~1.0.0" - -commander@^2.11.0, commander@^2.12.1, commander@^2.20.0: - version "2.20.3" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" - integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== - -commondir@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/commondir/-/commondir-1.0.1.tgz#ddd800da0c66127393cca5950ea968a3aaf1253b" - integrity sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs= - -component-bind@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/component-bind/-/component-bind-1.0.0.tgz#00c608ab7dcd93897c0009651b1d3a8e1e73bbd1" - integrity sha1-AMYIq33Nk4l8AAllGx06jh5zu9E= - -component-emitter@1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" - integrity sha1-E3kY1teCg/ffemt8WmPhQOaUJeY= - -component-emitter@^1.2.1, component-emitter@~1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" - integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== - -component-inherit@0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/component-inherit/-/component-inherit-0.0.3.tgz#645fc4adf58b72b649d5cae65135619db26ff143" - integrity sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM= - -compose-function@3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/compose-function/-/compose-function-3.0.3.tgz#9ed675f13cc54501d30950a486ff6a7ba3ab185f" - integrity sha1-ntZ18TzFRQHTCVCkhv9qe6OrGF8= - dependencies: - arity-n "^1.0.4" - -compressible@~2.0.16: - version "2.0.18" - resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" - integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg== - dependencies: - mime-db ">= 1.43.0 < 2" - -compression@^1.7.4: - version "1.7.4" - resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f" - integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ== - 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" - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= - -concat-stream@^1.5.0, concat-stream@^1.6.2: - version "1.6.2" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" - integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== - dependencies: - buffer-from "^1.0.0" - inherits "^2.0.3" - readable-stream "^2.2.2" - typedarray "^0.0.6" - -config-chain@^1.1.11: - version "1.1.12" - resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.12.tgz#0fde8d091200eb5e808caf25fe618c02f48e4efa" - integrity sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA== - dependencies: - ini "^1.3.4" - proto-list "~1.2.1" - -connect-history-api-fallback@^1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz#8b32089359308d111115d81cad3fceab888f97bc" - integrity sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg== - -connect@^3.7.0: - version "3.7.0" - resolved "https://registry.yarnpkg.com/connect/-/connect-3.7.0.tgz#5d49348910caa5e07a01800b030d0c35f20484f8" - integrity sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ== - dependencies: - debug "2.6.9" - finalhandler "1.1.2" - parseurl "~1.3.3" - utils-merge "1.0.1" - -console-browserify@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/console-browserify/-/console-browserify-1.2.0.tgz#67063cef57ceb6cf4993a2ab3a55840ae8c49336" - integrity sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA== - -constants-browserify@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" - integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U= - -content-disposition@0.5.3: - version "0.5.3" - resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.3.tgz#e130caf7e7279087c5616c2007d0485698984fbd" - integrity sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g== - dependencies: - safe-buffer "5.1.2" - -content-type@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" - integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== - -convert-source-map@1.7.0, convert-source-map@^1.5.1, convert-source-map@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.7.0.tgz#17a2cb882d7f77d3490585e2ce6c524424a3a442" - integrity sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA== - dependencies: - safe-buffer "~5.1.1" - -convert-source-map@^0.3.3: - version "0.3.5" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-0.3.5.tgz#f1d802950af7dd2631a1febe0596550c86ab3190" - integrity sha1-8dgClQr33SYxof6+BZZVDIarMZA= - -cookie-signature@1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c" - integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw= - -cookie@0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" - integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== - -cookie@~0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1" - integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA== - -copy-concurrently@^1.0.0: - version "1.0.5" - resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0" - integrity sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A== - dependencies: - aproba "^1.1.1" - fs-write-stream-atomic "^1.0.8" - iferr "^0.1.5" - mkdirp "^0.5.1" - rimraf "^2.5.4" - run-queue "^1.0.0" - -copy-descriptor@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" - integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= - -copy-webpack-plugin@6.2.1: - version "6.2.1" - resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-6.2.1.tgz#8015e4d5c5e637ab7b39c76daa9e03c7a4bf1ae5" - integrity sha512-VH2ZTMIBsx4p++Lmpg77adZ0KUyM5gFR/9cuTrbneNnJlcQXUFvsNariPqq2dq2kV3F2skHiDGPQCyKWy1+U0Q== - dependencies: - cacache "^15.0.5" - fast-glob "^3.2.4" - find-cache-dir "^3.3.1" - glob-parent "^5.1.1" - globby "^11.0.1" - loader-utils "^2.0.0" - normalize-path "^3.0.0" - p-limit "^3.0.2" - schema-utils "^3.0.0" - serialize-javascript "^5.0.1" - webpack-sources "^1.4.3" - -core-js-compat@^3.6.2: - version "3.9.1" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.9.1.tgz#4e572acfe90aff69d76d8c37759d21a5c59bb455" - integrity sha512-jXAirMQxrkbiiLsCx9bQPJFA6llDadKMpYrBJQJ3/c4/vsPP/fAf29h24tviRlvwUL6AmY5CHLu2GvjuYviQqA== - dependencies: - browserslist "^4.16.3" - semver "7.0.0" - -core-js@3.6.5: - version "3.6.5" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.6.5.tgz#7395dc273af37fb2e50e9bd3d9fe841285231d1a" - integrity sha512-vZVEEwZoIsI+vPEuoF9Iqf5H7/M3eeQqWlQnYa8FSKKePuYTf5MWnxb5SDAzCa60b3JBRS5g9b+Dq7b1y/RCrA== - -core-js@^3.6.5: - version "3.9.1" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.9.1.tgz#cec8de593db8eb2a85ffb0dbdeb312cb6e5460ae" - integrity sha512-gSjRvzkxQc1zjM/5paAmL4idJBFzuJoo+jDjF1tStYFMV2ERfD02HhahhCGXUyHxQRG4yFKVSdO6g62eoRMcDg== - -core-util-is@1.0.2, core-util-is@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= - -cosmiconfig@^5.0.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-5.2.1.tgz#040f726809c591e77a17c0a3626ca45b4f168b1a" - integrity sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA== - dependencies: - import-fresh "^2.0.0" - is-directory "^0.3.1" - js-yaml "^3.13.1" - parse-json "^4.0.0" - -cosmiconfig@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-7.0.0.tgz#ef9b44d773959cae63ddecd122de23853b60f8d3" - integrity sha512-pondGvTuVYDk++upghXJabWzL6Kxu6f26ljFw64Swq9v6sQPUL3EUlVDV56diOjpCayKihL6hVe8exIACU4XcA== - 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" - -create-ecdh@^4.0.0: - version "4.0.4" - resolved "https://registry.yarnpkg.com/create-ecdh/-/create-ecdh-4.0.4.tgz#d6e7f4bffa66736085a0762fd3a632684dabcc4e" - integrity sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A== - dependencies: - bn.js "^4.1.0" - elliptic "^6.5.3" - -create-hash@^1.1.0, create-hash@^1.1.2, create-hash@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/create-hash/-/create-hash-1.2.0.tgz#889078af11a63756bcfb59bd221996be3a9ef196" - integrity sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg== - dependencies: - cipher-base "^1.0.1" - inherits "^2.0.1" - md5.js "^1.3.4" - ripemd160 "^2.0.1" - sha.js "^2.4.0" - -create-hmac@^1.1.0, create-hmac@^1.1.4, create-hmac@^1.1.7: - version "1.1.7" - resolved "https://registry.yarnpkg.com/create-hmac/-/create-hmac-1.1.7.tgz#69170c78b3ab957147b2b8b04572e47ead2243ff" - integrity sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg== - dependencies: - cipher-base "^1.0.3" - create-hash "^1.1.0" - inherits "^2.0.1" - ripemd160 "^2.0.0" - safe-buffer "^5.0.1" - sha.js "^2.4.8" - -cross-spawn@^6.0.0: - version "6.0.5" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" - integrity sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ== - dependencies: - nice-try "^1.0.4" - path-key "^2.0.1" - semver "^5.5.0" - shebang-command "^1.2.0" - which "^1.2.9" - -crypto-browserify@^3.11.0: - version "3.12.0" - resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" - integrity sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg== - dependencies: - browserify-cipher "^1.0.0" - browserify-sign "^4.0.0" - create-ecdh "^4.0.0" - create-hash "^1.1.0" - create-hmac "^1.1.0" - diffie-hellman "^5.0.0" - inherits "^2.0.1" - pbkdf2 "^3.0.3" - public-encrypt "^4.0.0" - randombytes "^2.0.0" - randomfill "^1.0.3" - -css-color-names@0.0.4, css-color-names@^0.0.4: - version "0.0.4" - resolved "https://registry.yarnpkg.com/css-color-names/-/css-color-names-0.0.4.tgz#808adc2e79cf84738069b646cb20ec27beb629e0" - integrity sha1-gIrcLnnPhHOAabZGyyDsJ762KeA= - -css-declaration-sorter@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-4.0.1.tgz#c198940f63a76d7e36c1e71018b001721054cb22" - integrity sha512-BcxQSKTSEEQUftYpBVnsH4SF05NTuBokb19/sBt6asXGKZ/6VP7PLG1CBCkFDYOnhXhPh0jMhO6xZ71oYHXHBA== - dependencies: - postcss "^7.0.1" - timsort "^0.3.0" - -css-loader@4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/css-loader/-/css-loader-4.3.0.tgz#c888af64b2a5b2e85462c72c0f4a85c7e2e0821e" - integrity sha512-rdezjCjScIrsL8BSYszgT4s476IcNKt6yX69t0pHjJVnPUTDpn4WfIpDQTN3wCJvUvfsz/mFjuGOekf3PY3NUg== - dependencies: - camelcase "^6.0.0" - cssesc "^3.0.0" - icss-utils "^4.1.1" - loader-utils "^2.0.0" - postcss "^7.0.32" - postcss-modules-extract-imports "^2.0.0" - postcss-modules-local-by-default "^3.0.3" - postcss-modules-scope "^2.2.0" - postcss-modules-values "^3.0.0" - postcss-value-parser "^4.1.0" - schema-utils "^2.7.1" - semver "^7.3.2" - -css-parse@~2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/css-parse/-/css-parse-2.0.0.tgz#a468ee667c16d81ccf05c58c38d2a97c780dbfd4" - integrity sha1-pGjuZnwW2BzPBcWMONKpfHgNv9Q= - dependencies: - css "^2.0.0" - -css-select-base-adapter@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/css-select-base-adapter/-/css-select-base-adapter-0.1.1.tgz#3b2ff4972cc362ab88561507a95408a1432135d7" - integrity sha512-jQVeeRG70QI08vSTwf1jHxp74JoZsr2XSgETae8/xC8ovSnL2WF87GTLO86Sbwdt2lK4Umg4HnnwMO4YF3Ce7w== - -css-select@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/css-select/-/css-select-2.1.0.tgz#6a34653356635934a81baca68d0255432105dbef" - integrity sha512-Dqk7LQKpwLoH3VovzZnkzegqNSuAziQyNZUcrdDM401iY+R5NkGBXGmtO05/yaXQziALuPogeG0b7UAgjnTJTQ== - dependencies: - boolbase "^1.0.0" - css-what "^3.2.1" - domutils "^1.7.0" - nth-check "^1.0.2" - -css-selector-tokenizer@^0.7.1: - version "0.7.3" - resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.7.3.tgz#735f26186e67c749aaf275783405cf0661fae8f1" - integrity sha512-jWQv3oCEL5kMErj4wRnK/OPoBi0D+P1FR2cDCKYPaMeD2eW3/mttav8HT4hT1CKopiJI/psEULjkClhvJo4Lvg== - dependencies: - cssesc "^3.0.0" - fastparse "^1.1.2" - -css-tree@1.0.0-alpha.37: - version "1.0.0-alpha.37" - resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.0.0-alpha.37.tgz#98bebd62c4c1d9f960ec340cf9f7522e30709a22" - integrity sha512-DMxWJg0rnz7UgxKT0Q1HU/L9BeJI0M6ksor0OgqOnF+aRCDWg/N2641HmVyU9KVIu0OVVWOb2IpC9A+BJRnejg== - dependencies: - mdn-data "2.0.4" - source-map "^0.6.1" - -css-tree@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.2.tgz#9ae393b5dafd7dae8a622475caec78d3d8fbd7b5" - integrity sha512-wCoWush5Aeo48GLhfHPbmvZs59Z+M7k5+B1xDnXbdWNcEF423DoFdqSWE0PM5aNk5nI5cp1q7ms36zGApY/sKQ== - dependencies: - mdn-data "2.0.14" - source-map "^0.6.1" - -css-what@^3.2.1: - version "3.4.2" - resolved "https://registry.yarnpkg.com/css-what/-/css-what-3.4.2.tgz#ea7026fcb01777edbde52124e21f327e7ae950e4" - integrity sha512-ACUm3L0/jiZTqfzRM3Hi9Q8eZqd6IK37mMWPLz9PJxkLWllYeRf+EHUSHYEtFop2Eqytaq1FizFVh7XfBnXCDQ== - -css@^2.0.0: - version "2.2.4" - resolved "https://registry.yarnpkg.com/css/-/css-2.2.4.tgz#c646755c73971f2bba6a601e2cf2fd71b1298929" - integrity sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw== - dependencies: - inherits "^2.0.3" - source-map "^0.6.1" - source-map-resolve "^0.5.2" - urix "^0.1.0" - -cssauron@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/cssauron/-/cssauron-1.4.0.tgz#a6602dff7e04a8306dc0db9a551e92e8b5662ad8" - integrity sha1-pmAt/34EqDBtwNuaVR6S6LVmKtg= - dependencies: - through X.X.X - -cssesc@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" - integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== - -cssnano-preset-default@^4.0.7: - version "4.0.7" - resolved "https://registry.yarnpkg.com/cssnano-preset-default/-/cssnano-preset-default-4.0.7.tgz#51ec662ccfca0f88b396dcd9679cdb931be17f76" - integrity sha512-x0YHHx2h6p0fCl1zY9L9roD7rnlltugGu7zXSKQx6k2rYw0Hi3IqxcoAGF7u9Q5w1nt7vK0ulxV8Lo+EvllGsA== - dependencies: - css-declaration-sorter "^4.0.1" - cssnano-util-raw-cache "^4.0.1" - postcss "^7.0.0" - postcss-calc "^7.0.1" - postcss-colormin "^4.0.3" - postcss-convert-values "^4.0.1" - postcss-discard-comments "^4.0.2" - postcss-discard-duplicates "^4.0.2" - postcss-discard-empty "^4.0.1" - postcss-discard-overridden "^4.0.1" - postcss-merge-longhand "^4.0.11" - postcss-merge-rules "^4.0.3" - postcss-minify-font-values "^4.0.2" - postcss-minify-gradients "^4.0.2" - postcss-minify-params "^4.0.2" - postcss-minify-selectors "^4.0.2" - postcss-normalize-charset "^4.0.1" - postcss-normalize-display-values "^4.0.2" - postcss-normalize-positions "^4.0.2" - postcss-normalize-repeat-style "^4.0.2" - postcss-normalize-string "^4.0.2" - postcss-normalize-timing-functions "^4.0.2" - postcss-normalize-unicode "^4.0.1" - postcss-normalize-url "^4.0.1" - postcss-normalize-whitespace "^4.0.2" - postcss-ordered-values "^4.1.2" - postcss-reduce-initial "^4.0.3" - postcss-reduce-transforms "^4.0.2" - postcss-svgo "^4.0.2" - postcss-unique-selectors "^4.0.1" - -cssnano-util-get-arguments@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/cssnano-util-get-arguments/-/cssnano-util-get-arguments-4.0.0.tgz#ed3a08299f21d75741b20f3b81f194ed49cc150f" - integrity sha1-7ToIKZ8h11dBsg87gfGU7UnMFQ8= - -cssnano-util-get-match@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/cssnano-util-get-match/-/cssnano-util-get-match-4.0.0.tgz#c0e4ca07f5386bb17ec5e52250b4f5961365156d" - integrity sha1-wOTKB/U4a7F+xeUiULT1lhNlFW0= - -cssnano-util-raw-cache@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/cssnano-util-raw-cache/-/cssnano-util-raw-cache-4.0.1.tgz#b26d5fd5f72a11dfe7a7846fb4c67260f96bf282" - integrity sha512-qLuYtWK2b2Dy55I8ZX3ky1Z16WYsx544Q0UWViebptpwn/xDBmog2TLg4f+DBMg1rJ6JDWtn96WHbOKDWt1WQA== - dependencies: - postcss "^7.0.0" - -cssnano-util-same-parent@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/cssnano-util-same-parent/-/cssnano-util-same-parent-4.0.1.tgz#574082fb2859d2db433855835d9a8456ea18bbf3" - integrity sha512-WcKx5OY+KoSIAxBW6UBBRay1U6vkYheCdjyVNDm85zt5K9mHoGOfsOsqIszfAqrQQFIIKgjh2+FDgIj/zsl21Q== - -cssnano@4.1.10: - version "4.1.10" - resolved "https://registry.yarnpkg.com/cssnano/-/cssnano-4.1.10.tgz#0ac41f0b13d13d465487e111b778d42da631b8b2" - integrity sha512-5wny+F6H4/8RgNlaqab4ktc3e0/blKutmq8yNlBFXA//nSFFAqAngjNVRzUvCgYROULmZZUoosL/KSoZo5aUaQ== - dependencies: - cosmiconfig "^5.0.0" - cssnano-preset-default "^4.0.7" - is-resolvable "^1.0.0" - postcss "^7.0.0" - -csso@^4.0.2: - version "4.2.0" - resolved "https://registry.yarnpkg.com/csso/-/csso-4.2.0.tgz#ea3a561346e8dc9f546d6febedd50187cf389529" - integrity sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA== - dependencies: - css-tree "^1.1.2" - -custom-event@~1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/custom-event/-/custom-event-1.0.1.tgz#5d02a46850adf1b4a317946a3928fccb5bfd0425" - integrity sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU= - -cyclist@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/cyclist/-/cyclist-1.0.1.tgz#596e9698fd0c80e12038c2b82d6eb1b35b6224d9" - integrity sha1-WW6WmP0MgOEgOMK4LW6xs1tiJNk= - -d@1, d@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" - integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== - dependencies: - es5-ext "^0.10.50" - type "^1.0.1" - -damerau-levenshtein@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.6.tgz#143c1641cb3d85c60c32329e26899adea8701791" - integrity sha512-JVrozIeElnj3QzfUIt8tB8YMluBJom4Vw9qTPpjGYQ9fYlB3D/rb6OordUxf3xeFB35LKWs0xqcO5U6ySvBtug== - -dashdash@^1.12.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" - integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= - dependencies: - assert-plus "^1.0.0" - -date-format@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/date-format/-/date-format-2.1.0.tgz#31d5b5ea211cf5fd764cd38baf9d033df7e125cf" - integrity sha512-bYQuGLeFxhkxNOF3rcMtiZxvCBAquGzZm6oWA1oZ0g2THUzivaRhv8uOhdr19LmoobSOLoIAxeUK2RdbM8IFTA== - -date-format@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/date-format/-/date-format-3.0.0.tgz#eb8780365c7d2b1511078fb491e6479780f3ad95" - integrity sha512-eyTcpKOcamdhWJXj56DpQMo1ylSQpcGtGKXcU0Tb97+K56/CF5amAqqqNj0+KvA0iw2ynxtHWFsPDSClCxe48w== - -debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.9: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - -debug@3.1.0, debug@~3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" - integrity sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g== - dependencies: - ms "2.0.0" - -debug@4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.2.0.tgz#7f150f93920e94c58f5574c2fd01a3110effe7f1" - integrity sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg== - dependencies: - ms "2.1.2" - -debug@^3.1.0, debug@^3.1.1, debug@^3.2.5: - version "3.2.7" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" - integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== - dependencies: - ms "^2.1.1" - -debug@^4.1.0, debug@^4.1.1: - version "4.3.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz#f0d229c505e0c6d8c49ac553d1b13dc183f6b2ee" - integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== - dependencies: - ms "2.1.2" - -debug@~4.1.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" - integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== - dependencies: - ms "^2.1.1" - -decamelize@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" - integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= - -decode-uri-component@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" - integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= - -decompress-response@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3" - integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= - dependencies: - mimic-response "^1.0.0" - -deep-equal@^1.0.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a" - integrity sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g== - dependencies: - is-arguments "^1.0.4" - is-date-object "^1.0.1" - is-regex "^1.0.4" - object-is "^1.0.1" - object-keys "^1.1.1" - regexp.prototype.flags "^1.2.0" - -default-gateway@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-4.2.0.tgz#167104c7500c2115f6dd69b0a536bb8ed720552b" - integrity sha512-h6sMrVB1VMWVrW13mSc6ia/DwYYw5MN6+exNu1OaJeFac5aSAvwM7lZ0NVfTABuSkQelr4h5oebg3KB1XPdjgA== - dependencies: - execa "^1.0.0" - ip-regex "^2.1.0" - -defaults@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d" - integrity sha1-xlYFHpgX2f8I7YgUd/P+QBnz730= - dependencies: - clone "^1.0.2" - -defer-to-connect@^1.0.1: - version "1.1.3" - resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" - integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== - -define-properties@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" - integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== - dependencies: - object-keys "^1.0.12" - -define-property@^0.2.5: - version "0.2.5" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" - integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= - dependencies: - is-descriptor "^0.1.0" - -define-property@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" - integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= - dependencies: - is-descriptor "^1.0.0" - -define-property@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" - integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== - dependencies: - is-descriptor "^1.0.2" - isobject "^3.0.1" - -del@^2.2.0: - version "2.2.2" - resolved "https://registry.yarnpkg.com/del/-/del-2.2.2.tgz#c12c981d067846c84bcaf862cff930d907ffd1a8" - integrity sha1-wSyYHQZ4RshLyvhiz/kw2Qf/0ag= - dependencies: - globby "^5.0.0" - is-path-cwd "^1.0.0" - is-path-in-cwd "^1.0.0" - object-assign "^4.0.1" - pify "^2.0.0" - pinkie-promise "^2.0.0" - rimraf "^2.2.8" - -del@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/del/-/del-4.1.1.tgz#9e8f117222ea44a31ff3a156c049b99052a9f0b4" - integrity sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ== - dependencies: - "@types/glob" "^7.1.1" - globby "^6.1.0" - is-path-cwd "^2.0.0" - is-path-in-cwd "^2.0.0" - p-map "^2.0.0" - pify "^4.0.1" - rimraf "^2.6.3" - -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk= - -depd@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9" - integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= - -dependency-graph@^0.7.2: - version "0.7.2" - resolved "https://registry.yarnpkg.com/dependency-graph/-/dependency-graph-0.7.2.tgz#91db9de6eb72699209d88aea4c1fd5221cac1c49" - integrity sha512-KqtH4/EZdtdfWX0p6MGP9jljvxSY6msy/pRUD4jgNwVpv3v1QmNLlsB3LDSSUg79BRVSn7jI1QPRtArGABovAQ== - -des.js@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/des.js/-/des.js-1.0.1.tgz#5382142e1bdc53f85d86d53e5f4aa7deb91e0843" - integrity sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA== - dependencies: - inherits "^2.0.1" - minimalistic-assert "^1.0.0" - -destroy@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" - integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= - -detect-node@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.4.tgz#014ee8f8f669c5c58023da64b8179c083a28c46c" - integrity sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw== - -di@^0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c" - integrity sha1-gGZJMmzqp8qjMG112YXqJ0i6kTw= - -diff@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" - integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== - -diffie-hellman@^5.0.0: - version "5.0.3" - resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875" - integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg== - dependencies: - bn.js "^4.1.0" - miller-rabin "^4.0.0" - randombytes "^2.0.0" - -dir-glob@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" - integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== - dependencies: - path-type "^4.0.0" - -dns-equal@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/dns-equal/-/dns-equal-1.0.0.tgz#b39e7f1da6eb0a75ba9c17324b34753c47e0654d" - integrity sha1-s55/HabrCnW6nBcySzR1PEfgZU0= - -dns-packet@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/dns-packet/-/dns-packet-1.3.1.tgz#12aa426981075be500b910eedcd0b47dd7deda5a" - integrity sha512-0UxfQkMhYAUaZI+xrNZOz/as5KgDU0M/fQ9b6SpkyLbk3GEswDi6PADJVaYJradtRVsRIlF1zLyOodbcTCDzUg== - dependencies: - ip "^1.1.0" - safe-buffer "^5.0.1" - -dns-txt@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/dns-txt/-/dns-txt-2.0.2.tgz#b91d806f5d27188e4ab3e7d107d881a1cc4642b6" - integrity sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY= - dependencies: - buffer-indexof "^1.0.0" - -dom-serialize@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/dom-serialize/-/dom-serialize-2.2.1.tgz#562ae8999f44be5ea3076f5419dcd59eb43ac95b" - integrity sha1-ViromZ9Evl6jB29UGdzVnrQ6yVs= - dependencies: - custom-event "~1.0.0" - ent "~2.2.0" - extend "^3.0.0" - void-elements "^2.0.0" - -dom-serializer@0: - version "0.2.2" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51" - integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g== - dependencies: - domelementtype "^2.0.1" - entities "^2.0.0" - -domain-browser@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.2.0.tgz#3d31f50191a6749dd1375a7f522e823d42e54eda" - integrity sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA== - -domelementtype@1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" - integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== - -domelementtype@^2.0.1: - version "2.1.0" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.1.0.tgz#a851c080a6d1c3d94344aed151d99f669edf585e" - integrity sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w== - -domutils@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" - integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg== - dependencies: - dom-serializer "0" - domelementtype "1" - -dot-prop@^5.2.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88" - integrity sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q== - dependencies: - is-obj "^2.0.0" - -duplexer3@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2" - integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= - -duplexify@^3.4.2, duplexify@^3.6.0: - version "3.7.1" - resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-3.7.1.tgz#2a4df5317f6ccfd91f86d6fd25d8d8a103b88309" - integrity sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g== - dependencies: - end-of-stream "^1.0.0" - inherits "^2.0.1" - readable-stream "^2.0.0" - stream-shift "^1.0.0" - -ecc-jsbn@~0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" - integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= - dependencies: - jsbn "~0.1.0" - safer-buffer "^2.1.0" - -ee-first@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" - integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0= - -electron-to-chromium@^1.3.649: - version "1.3.687" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.687.tgz#c336184b7ab70427ffe2ee79eaeaedbc1ad8c374" - integrity sha512-IpzksdQNl3wdgkzf7dnA7/v10w0Utf1dF2L+B4+gKrloBrxCut+au+kky3PYvle3RMdSxZP+UiCZtLbcYRxSNQ== - -electron@11.1.1: - version "11.1.1" - resolved "https://registry.yarnpkg.com/electron/-/electron-11.1.1.tgz#188f036f8282798398dca9513e9bb3b10213e3aa" - integrity sha512-tlbex3xosJgfileN6BAQRotevPRXB/wQIq48QeQ08tUJJrXwE72c8smsM/hbHx5eDgnbfJ2G3a60PmRjHU2NhA== - dependencies: - "@electron/get" "^1.0.1" - "@types/node" "^12.0.12" - extract-zip "^1.0.3" - -elliptic@^6.5.3: - version "6.5.4" - resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" - integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== - dependencies: - bn.js "^4.11.9" - brorand "^1.1.0" - hash.js "^1.0.0" - hmac-drbg "^1.0.1" - inherits "^2.0.4" - minimalistic-assert "^1.0.1" - minimalistic-crypto-utils "^1.0.1" - -emoji-regex@^7.0.1: - version "7.0.3" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" - integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== - -emoji-regex@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" - integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== - -emojis-list@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" - integrity sha1-TapNnbAPmBmIDHn6RXrlsJof04k= - -emojis-list@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78" - integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q== - -encodeurl@^1.0.2, encodeurl@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" - integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= - -encoding@^0.1.11: - version "0.1.13" - resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" - integrity sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A== - dependencies: - iconv-lite "^0.6.2" - -end-of-stream@^1.0.0, end-of-stream@^1.1.0: - version "1.4.4" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" - integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== - dependencies: - once "^1.4.0" - -engine.io-client@~3.5.0: - version "3.5.1" - resolved "https://registry.yarnpkg.com/engine.io-client/-/engine.io-client-3.5.1.tgz#b500458a39c0cd197a921e0e759721a746d0bdb9" - integrity sha512-oVu9kBkGbcggulyVF0kz6BV3ganqUeqXvD79WOFKa+11oK692w1NyFkuEj4xrkFRpZhn92QOqTk4RQq5LiBXbQ== - dependencies: - component-emitter "~1.3.0" - component-inherit "0.0.3" - debug "~3.1.0" - engine.io-parser "~2.2.0" - has-cors "1.1.0" - indexof "0.0.1" - parseqs "0.0.6" - parseuri "0.0.6" - ws "~7.4.2" - xmlhttprequest-ssl "~1.5.4" - yeast "0.1.2" - -engine.io-parser@~2.2.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-2.2.1.tgz#57ce5611d9370ee94f99641b589f94c97e4f5da7" - integrity sha512-x+dN/fBH8Ro8TFwJ+rkB2AmuVw9Yu2mockR/p3W8f8YtExwFgDvBDi0GWyb4ZLkpahtDGZgtr3zLovanJghPqg== - dependencies: - after "0.8.2" - arraybuffer.slice "~0.0.7" - base64-arraybuffer "0.1.4" - blob "0.0.5" - has-binary2 "~1.0.2" - -engine.io@~3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-3.5.0.tgz#9d6b985c8a39b1fe87cd91eb014de0552259821b" - integrity sha512-21HlvPUKaitDGE4GXNtQ7PLP0Sz4aWLddMPw2VTyFz1FVZqu/kZsJUO8WNpKuE/OCL7nkfRaOui2ZCJloGznGA== - dependencies: - accepts "~1.3.4" - base64id "2.0.0" - cookie "~0.4.1" - debug "~4.1.0" - engine.io-parser "~2.2.0" - ws "~7.4.2" - -enhanced-resolve@5.3.1: - version "5.3.1" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.3.1.tgz#3f988d0d7775bdc2d96ede321dc81f8249492f57" - integrity sha512-G1XD3MRGrGfNcf6Hg0LVZG7GIKcYkbfHa5QMxt1HDUTdYoXH0JR1xXyg+MaKLF73E9A27uWNVxvFivNRYeUB6w== - dependencies: - graceful-fs "^4.2.4" - tapable "^2.0.0" - -enhanced-resolve@^4.3.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-4.5.0.tgz#2f3cfd84dbe3b487f18f2db2ef1e064a571ca5ec" - integrity sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg== - dependencies: - graceful-fs "^4.1.2" - memory-fs "^0.5.0" - tapable "^1.0.0" - -ent@~2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/ent/-/ent-2.2.0.tgz#e964219325a21d05f44466a2f686ed6ce5f5dd1d" - integrity sha1-6WQhkyWiHQX0RGai9obtbOX13R0= - -entities@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" - integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== - -env-paths@^2.2.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" - integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== - -err-code@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/err-code/-/err-code-1.1.2.tgz#06e0116d3028f6aef4806849eb0ea6a748ae6960" - integrity sha1-BuARbTAo9q70gGhJ6w6mp0iuaWA= - -errno@^0.1.1, errno@^0.1.3, errno@~0.1.7: - version "0.1.8" - resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f" - integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A== - dependencies: - prr "~1.0.1" - -error-ex@^1.3.1: - version "1.3.2" - resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" - integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== - dependencies: - is-arrayish "^0.2.1" - -es-abstract@^1.17.2, es-abstract@^1.18.0-next.2: - version "1.18.0" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0.tgz#ab80b359eecb7ede4c298000390bc5ac3ec7b5a4" - integrity sha512-LJzK7MrQa8TS0ja2w3YNLzUgJCGPdPOV1yVvezjNnS89D+VR08+Szt2mz3YB2Dck/+w5tfIq/RoUAFqJJGM2yw== - dependencies: - call-bind "^1.0.2" - es-to-primitive "^1.2.1" - function-bind "^1.1.1" - get-intrinsic "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.2" - is-callable "^1.2.3" - is-negative-zero "^2.0.1" - is-regex "^1.1.2" - is-string "^1.0.5" - object-inspect "^1.9.0" - object-keys "^1.1.1" - object.assign "^4.1.2" - string.prototype.trimend "^1.0.4" - string.prototype.trimstart "^1.0.4" - unbox-primitive "^1.0.0" - -es-to-primitive@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" - integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== - dependencies: - is-callable "^1.1.4" - is-date-object "^1.0.1" - is-symbol "^1.0.2" - -es5-ext@^0.10.35, es5-ext@^0.10.50: - version "0.10.53" - resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1" - integrity sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q== - dependencies: - es6-iterator "~2.0.3" - es6-symbol "~3.1.3" - next-tick "~1.0.0" - -es6-error@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" - integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== - -es6-iterator@2.0.3, es6-iterator@~2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" - integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c= - dependencies: - d "1" - es5-ext "^0.10.35" - es6-symbol "^3.1.1" - -es6-promise@^4.0.3: - version "4.2.8" - resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" - integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== - -es6-promisify@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" - integrity sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM= - dependencies: - es6-promise "^4.0.3" - -es6-symbol@^3.1.1, es6-symbol@~3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" - integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== - dependencies: - d "^1.0.1" - ext "^1.1.2" - -escalade@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" - integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== - -escape-html@~1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" - integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= - -escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= - -escape-string-regexp@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" - integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== - -eslint-scope@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-4.0.3.tgz#ca03833310f6889a3264781aa82e63eb9cfe7848" - integrity sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg== - dependencies: - esrecurse "^4.1.0" - estraverse "^4.1.1" - -esprima@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" - integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== - -esrecurse@^4.1.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" - integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== - dependencies: - estraverse "^5.2.0" - -estraverse@^4.1.1: - version "4.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" - integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== - -estraverse@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" - integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== - -esutils@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" - integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== - -etag@~1.8.1: - version "1.8.1" - resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" - integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= - -eventemitter3@^4.0.0: - version "4.0.7" - resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" - integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== - -events@^3.0.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" - integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== - -eventsource@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/eventsource/-/eventsource-1.0.7.tgz#8fbc72c93fcd34088090bc0a4e64f4b5cee6d8d0" - integrity sha512-4Ln17+vVT0k8aWq+t/bF5arcS3EpT9gYtW66EPacdj/mAFevznsnyoHLPy2BA8gbIQeIHoPsvwmfBftfcG//BQ== - dependencies: - original "^1.0.0" - -evp_bytestokey@^1.0.0, evp_bytestokey@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz#7fcbdb198dc71959432efe13842684e0525acb02" - integrity sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA== - dependencies: - md5.js "^1.3.4" - safe-buffer "^5.1.1" - -execa@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" - integrity sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA== - dependencies: - cross-spawn "^6.0.0" - get-stream "^4.0.0" - is-stream "^1.1.0" - npm-run-path "^2.0.0" - p-finally "^1.0.0" - signal-exit "^3.0.0" - strip-eof "^1.0.0" - -exit@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" - integrity sha1-BjJjj42HfMghB9MKD/8aF8uhzQw= - -expand-brackets@^2.1.4: - version "2.1.4" - resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" - integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= - dependencies: - debug "^2.3.3" - define-property "^0.2.5" - extend-shallow "^2.0.1" - posix-character-classes "^0.1.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - -express@^4.17.1: - version "4.17.1" - resolved "https://registry.yarnpkg.com/express/-/express-4.17.1.tgz#4491fc38605cf51f8629d39c2b5d026f98a4c134" - integrity sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g== - dependencies: - accepts "~1.3.7" - array-flatten "1.1.1" - body-parser "1.19.0" - content-disposition "0.5.3" - content-type "~1.0.4" - cookie "0.4.0" - cookie-signature "1.0.6" - debug "2.6.9" - depd "~1.1.2" - encodeurl "~1.0.2" - escape-html "~1.0.3" - etag "~1.8.1" - finalhandler "~1.1.2" - fresh "0.5.2" - merge-descriptors "1.0.1" - methods "~1.1.2" - on-finished "~2.3.0" - parseurl "~1.3.3" - path-to-regexp "0.1.7" - proxy-addr "~2.0.5" - qs "6.7.0" - range-parser "~1.2.1" - safe-buffer "5.1.2" - send "0.17.1" - serve-static "1.14.1" - setprototypeof "1.1.1" - statuses "~1.5.0" - type-is "~1.6.18" - utils-merge "1.0.1" - vary "~1.1.2" - -ext@^1.1.2: - version "1.4.0" - resolved "https://registry.yarnpkg.com/ext/-/ext-1.4.0.tgz#89ae7a07158f79d35517882904324077e4379244" - integrity sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A== - dependencies: - type "^2.0.0" - -extend-shallow@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" - integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= - dependencies: - is-extendable "^0.1.0" - -extend-shallow@^3.0.0, extend-shallow@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" - integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= - dependencies: - assign-symbols "^1.0.0" - is-extendable "^1.0.1" - -extend@^3.0.0, extend@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" - integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== - -external-editor@^3.0.3: - version "3.1.0" - resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" - integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== - dependencies: - chardet "^0.7.0" - iconv-lite "^0.4.24" - tmp "^0.0.33" - -extglob@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" - integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== - dependencies: - array-unique "^0.3.2" - define-property "^1.0.0" - expand-brackets "^2.1.4" - extend-shallow "^2.0.1" - fragment-cache "^0.2.1" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - -extract-zip@^1.0.3: - version "1.7.0" - resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.7.0.tgz#556cc3ae9df7f452c493a0cfb51cc30277940927" - integrity sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA== - dependencies: - concat-stream "^1.6.2" - debug "^2.6.9" - mkdirp "^0.5.4" - yauzl "^2.10.0" - -extsprintf@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05" - integrity sha1-lpGEQOMEGnpBT4xS48V06zw+HgU= - -extsprintf@^1.2.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f" - integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8= - -fast-deep-equal@^3.1.1: - version "3.1.3" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" - integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== - -fast-glob@^3.1.1, fast-glob@^3.2.4: - version "3.2.5" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.5.tgz#7939af2a656de79a4f1901903ee8adcaa7cb9661" - integrity sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg== - dependencies: - "@nodelib/fs.stat" "^2.0.2" - "@nodelib/fs.walk" "^1.2.3" - glob-parent "^5.1.0" - merge2 "^1.3.0" - micromatch "^4.0.2" - picomatch "^2.2.1" - -fast-json-stable-stringify@2.1.0, fast-json-stable-stringify@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" - integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== - -fastparse@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.2.tgz#91728c5a5942eced8531283c79441ee4122c35a9" - integrity sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ== - -fastq@^1.6.0: - version "1.11.0" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.11.0.tgz#bb9fb955a07130a918eb63c1f5161cc32a5d0858" - integrity sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g== - dependencies: - reusify "^1.0.4" - -faye-websocket@^0.10.0: - version "0.10.0" - resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.10.0.tgz#4e492f8d04dfb6f89003507f6edbf2d501e7c6f4" - integrity sha1-TkkvjQTftviQA1B/btvy1QHnxvQ= - dependencies: - websocket-driver ">=0.5.1" - -faye-websocket@~0.11.1: - version "0.11.3" - resolved "https://registry.yarnpkg.com/faye-websocket/-/faye-websocket-0.11.3.tgz#5c0e9a8968e8912c286639fde977a8b209f2508e" - integrity sha512-D2y4bovYpzziGgbHYtGCMjlJM36vAl/y+xUyn1C+FVx8szd1E+86KwVw6XvYSzOP8iMpm1X0I4xJD+QtUb36OA== - dependencies: - websocket-driver ">=0.5.1" - -fd-slicer@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" - integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= - dependencies: - pend "~1.2.0" - -figgy-pudding@^3.4.1, figgy-pudding@^3.5.1: - version "3.5.2" - resolved "https://registry.yarnpkg.com/figgy-pudding/-/figgy-pudding-3.5.2.tgz#b4eee8148abb01dcf1d1ac34367d59e12fa61d6e" - integrity sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw== - -figures@^3.0.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" - integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== - dependencies: - escape-string-regexp "^1.0.5" - -file-loader@6.1.1: - version "6.1.1" - resolved "https://registry.yarnpkg.com/file-loader/-/file-loader-6.1.1.tgz#a6f29dfb3f5933a1c350b2dbaa20ac5be0539baa" - integrity sha512-Klt8C4BjWSXYQAfhpYYkG4qHNTna4toMHEbWrI5IuVoxbU6uiDKeKAP99R8mmbJi3lvewn/jQBOgU4+NS3tDQw== - dependencies: - loader-utils "^2.0.0" - schema-utils "^3.0.0" - -file-uri-to-path@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" - integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== - -fill-range@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" - integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= - dependencies: - extend-shallow "^2.0.1" - is-number "^3.0.0" - repeat-string "^1.6.1" - to-regex-range "^2.1.0" - -fill-range@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" - integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== - dependencies: - to-regex-range "^5.0.1" - -finalhandler@1.1.2, finalhandler@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" - integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== - dependencies: - debug "2.6.9" - encodeurl "~1.0.2" - escape-html "~1.0.3" - on-finished "~2.3.0" - parseurl "~1.3.3" - statuses "~1.5.0" - unpipe "~1.0.0" - -find-cache-dir@3.3.1, find-cache-dir@^3.3.1: - version "3.3.1" - resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.1.tgz#89b33fad4a4670daa94f855f7fbe31d6d84fe880" - integrity sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ== - dependencies: - commondir "^1.0.1" - make-dir "^3.0.2" - pkg-dir "^4.1.0" - -find-cache-dir@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-2.1.0.tgz#8d0f94cd13fe43c6c7c261a0d86115ca918c05f7" - integrity sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ== - dependencies: - commondir "^1.0.1" - make-dir "^2.0.0" - pkg-dir "^3.0.0" - -find-up@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-3.0.0.tgz#49169f1d7993430646da61ecc5ae355c21c97b73" - integrity sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg== - dependencies: - locate-path "^3.0.0" - -find-up@^4.0.0, find-up@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" - integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== - dependencies: - locate-path "^5.0.0" - path-exists "^4.0.0" - -flatted@^2.0.1, flatted@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.2.tgz#4575b21e2bcee7434aa9be662f4b7b5f9c2b5138" - integrity sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA== - -flush-write-stream@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8" - integrity sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w== - dependencies: - inherits "^2.0.3" - readable-stream "^2.3.6" - -follow-redirects@^1.0.0: - version "1.13.3" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.3.tgz#e5598ad50174c1bc4e872301e82ac2cd97f90267" - integrity sha512-DUgl6+HDzB0iEptNQEXLx/KhTmDb8tZUHSeLqpnjpknR70H0nC2t9N73BK6fN4hOvJ84pKlIQVQ4k5FFlBedKA== - -for-in@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" - integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= - -forever-agent@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" - integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE= - -form-data@~2.3.2: - version "2.3.3" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6" - integrity sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.6" - mime-types "^2.1.12" - -forwarded@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84" - integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ= - -fragment-cache@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" - integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= - dependencies: - map-cache "^0.2.2" - -fresh@0.5.2: - version "0.5.2" - resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" - integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= - -from2@^2.1.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af" - integrity sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8= - dependencies: - inherits "^2.0.1" - readable-stream "^2.0.0" - -fs-extra@4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-4.0.2.tgz#f91704c53d1b461f893452b0c307d9997647ab6b" - integrity sha1-+RcExT0bRh+JNFKwwwfZmXZHq2s= - dependencies: - graceful-fs "^4.1.2" - jsonfile "^4.0.0" - universalify "^0.1.0" - -fs-extra@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" - integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== - dependencies: - graceful-fs "^4.2.0" - jsonfile "^4.0.0" - universalify "^0.1.0" - -fs-minipass@^1.2.5: - version "1.2.7" - resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-1.2.7.tgz#ccff8570841e7fe4265693da88936c55aed7f7c7" - integrity sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA== - dependencies: - minipass "^2.6.0" - -fs-minipass@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" - integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== - dependencies: - minipass "^3.0.0" - -fs-write-stream-atomic@^1.0.8: - version "1.0.10" - resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9" - integrity sha1-tH31NJPvkR33VzHnCp3tAYnbQMk= - dependencies: - graceful-fs "^4.1.2" - iferr "^0.1.5" - imurmurhash "^0.1.4" - readable-stream "1 || 2" - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= - -fsevents@^1.2.7: - version "1.2.13" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.2.13.tgz#f325cb0455592428bcf11b383370ef70e3bfcc38" - integrity sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw== - dependencies: - bindings "^1.5.0" - nan "^2.12.1" - -fsevents@~2.1.2: - version "2.1.3" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.3.tgz#fb738703ae8d2f9fe900c33836ddebee8b97f23e" - integrity sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ== - -fsevents@~2.3.1: - version "2.3.2" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" - integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== - -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== - -genfun@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/genfun/-/genfun-5.0.0.tgz#9dd9710a06900a5c4a5bf57aca5da4e52fe76537" - integrity sha512-KGDOARWVga7+rnB3z9Sd2Letx515owfk0hSxHGuqjANb1M+x2bGZGqHLiozPsYMdM2OubeMni/Hpwmjq6qIUhA== - -gensync@^1.0.0-beta.1, gensync@^1.0.0-beta.2: - version "1.0.0-beta.2" - resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" - integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== - -get-caller-file@^2.0.1, get-caller-file@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" - integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== - -get-intrinsic@^1.0.2, get-intrinsic@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" - integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== - dependencies: - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.1" - -get-stream@^4.0.0, get-stream@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" - integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== - dependencies: - pump "^3.0.0" - -get-stream@^5.1.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3" - integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== - dependencies: - pump "^3.0.0" - -get-value@^2.0.3, get-value@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" - integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= - -getpass@^0.1.1: - version "0.1.7" - resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" - integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= - dependencies: - assert-plus "^1.0.0" - -glob-parent@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" - integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4= - dependencies: - is-glob "^3.1.0" - path-dirname "^1.0.0" - -glob-parent@^5.1.0, glob-parent@^5.1.1, glob-parent@~5.1.0: - version "5.1.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" - integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== - dependencies: - is-glob "^4.0.1" - -glob@7.1.6, glob@^7.0.3, glob@^7.0.6, glob@^7.1.1, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6: - version "7.1.6" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" - integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - -global-agent@^2.0.2: - version "2.1.12" - resolved "https://registry.yarnpkg.com/global-agent/-/global-agent-2.1.12.tgz#e4ae3812b731a9e81cbf825f9377ef450a8e4195" - integrity sha512-caAljRMS/qcDo69X9BfkgrihGUgGx44Fb4QQToNQjsiWh+YlQ66uqYVAdA8Olqit+5Ng0nkz09je3ZzANMZcjg== - dependencies: - boolean "^3.0.1" - core-js "^3.6.5" - es6-error "^4.1.1" - matcher "^3.0.0" - roarr "^2.15.3" - semver "^7.3.2" - serialize-error "^7.0.1" - -global-tunnel-ng@^2.7.1: - version "2.7.1" - resolved "https://registry.yarnpkg.com/global-tunnel-ng/-/global-tunnel-ng-2.7.1.tgz#d03b5102dfde3a69914f5ee7d86761ca35d57d8f" - integrity sha512-4s+DyciWBV0eK148wqXxcmVAbFVPqtc3sEtUE/GTQfuU80rySLcMhUmHKSHI7/LDj8q0gDYI1lIhRRB7ieRAqg== - dependencies: - encodeurl "^1.0.2" - lodash "^4.17.10" - npm-conf "^1.1.3" - tunnel "^0.0.6" - -globals@^11.1.0: - version "11.12.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" - integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== - -globalthis@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.2.tgz#2a235d34f4d8036219f7e34929b5de9e18166b8b" - integrity sha512-ZQnSFO1la8P7auIOQECnm0sSuoMeaSq0EEdXMBFF2QJO4uNcwbyhSgG3MruWNbFTqCLmxVwGOl7LZ9kASvHdeQ== - dependencies: - define-properties "^1.1.3" - -globby@^11.0.1: - version "11.0.2" - resolved "https://registry.yarnpkg.com/globby/-/globby-11.0.2.tgz#1af538b766a3b540ebfb58a32b2e2d5897321d83" - integrity sha512-2ZThXDvvV8fYFRVIxnrMQBipZQDr7MxKAmQK1vujaj9/7eF0efG7BPUKJ7jP7G5SLF37xKDXvO4S/KKLj/Z0og== - dependencies: - array-union "^2.1.0" - dir-glob "^3.0.1" - fast-glob "^3.1.1" - ignore "^5.1.4" - merge2 "^1.3.0" - slash "^3.0.0" - -globby@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-5.0.0.tgz#ebd84667ca0dbb330b99bcfc68eac2bc54370e0d" - integrity sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0= - dependencies: - array-union "^1.0.1" - arrify "^1.0.0" - glob "^7.0.3" - object-assign "^4.0.1" - pify "^2.0.0" - pinkie-promise "^2.0.0" - -globby@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-6.1.0.tgz#f5a6d70e8395e21c858fb0489d64df02424d506c" - integrity sha1-9abXDoOV4hyFj7BInWTfAkJNUGw= - dependencies: - array-union "^1.0.1" - glob "^7.0.3" - object-assign "^4.0.1" - pify "^2.0.0" - pinkie-promise "^2.0.0" - -got@^9.6.0: - version "9.6.0" - resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz#edf45e7d67f99545705de1f7bbeeeb121765ed85" - integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== - dependencies: - "@sindresorhus/is" "^0.14.0" - "@szmarczak/http-timer" "^1.1.2" - cacheable-request "^6.0.0" - decompress-response "^3.3.0" - duplexer3 "^0.1.4" - get-stream "^4.1.0" - lowercase-keys "^1.0.1" - mimic-response "^1.0.1" - p-cancelable "^1.0.0" - to-readable-stream "^1.0.0" - url-parse-lax "^3.0.0" - -graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4: - version "4.2.6" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee" - integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== - -handle-thing@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e" - integrity sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg== - -har-schema@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" - integrity sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI= - -har-validator@~5.1.3: - version "5.1.5" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.1.5.tgz#1f0803b9f8cb20c0fa13822df1ecddb36bde1efd" - integrity sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w== - dependencies: - ajv "^6.12.3" - har-schema "^2.0.0" - -has-ansi@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" - integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE= - dependencies: - ansi-regex "^2.0.0" - -has-bigints@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" - integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== - -has-binary2@~1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has-binary2/-/has-binary2-1.0.3.tgz#7776ac627f3ea77250cfc332dab7ddf5e4f5d11d" - integrity sha512-G1LWKhDSvhGeAQ8mPVQlqNcOB2sJdwATtZKl2pDKKHfpf/rYj24lkinxf69blJbnsvtqqNU+L3SL50vzZhXOnw== - dependencies: - isarray "2.0.1" - -has-cors@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/has-cors/-/has-cors-1.1.0.tgz#5e474793f7ea9843d1bb99c23eef49ff126fff39" - integrity sha1-XkdHk/fqmEPRu5nCPu9J/xJv/zk= - -has-flag@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= - -has-flag@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" - integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== - -has-symbols@^1.0.0, has-symbols@^1.0.1, has-symbols@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" - integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== - -has-value@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" - integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= - dependencies: - get-value "^2.0.3" - has-values "^0.1.4" - isobject "^2.0.0" - -has-value@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" - integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= - dependencies: - get-value "^2.0.6" - has-values "^1.0.0" - isobject "^3.0.0" - -has-values@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" - integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= - -has-values@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" - integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= - dependencies: - is-number "^3.0.0" - kind-of "^4.0.0" - -has@^1.0.0, has@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" - integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== - dependencies: - function-bind "^1.1.1" - -hash-base@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.1.0.tgz#55c381d9e06e1d2997a883b4a3fddfe7f0d3af33" - integrity sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA== - dependencies: - inherits "^2.0.4" - readable-stream "^3.6.0" - safe-buffer "^5.2.0" - -hash.js@^1.0.0, hash.js@^1.0.3: - version "1.1.7" - resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" - integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== - dependencies: - inherits "^2.0.3" - minimalistic-assert "^1.0.1" - -hex-color-regex@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" - integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ== - -hmac-drbg@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/hmac-drbg/-/hmac-drbg-1.0.1.tgz#d2745701025a6c775a6c545793ed502fc0c649a1" - integrity sha1-0nRXAQJabHdabFRXk+1QL8DGSaE= - dependencies: - hash.js "^1.0.3" - minimalistic-assert "^1.0.0" - minimalistic-crypto-utils "^1.0.1" - -hosted-git-info@^2.1.4, hosted-git-info@^2.7.1: - version "2.8.8" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" - integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg== - -hosted-git-info@^3.0.6: - version "3.0.8" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-3.0.8.tgz#6e35d4cc87af2c5f816e4cb9ce350ba87a3f370d" - integrity sha512-aXpmwoOhRBrw6X3j0h5RloK4x1OzsxMPyxqIHyNfSe2pypkVTZFpEiRoSipPEPlMrh0HW/XsjkJ5WgnCirpNUw== - dependencies: - lru-cache "^6.0.0" - -hpack.js@^2.1.6: - version "2.1.6" - resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" - integrity sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI= - dependencies: - inherits "^2.0.1" - obuf "^1.0.0" - readable-stream "^2.0.1" - wbuf "^1.1.0" - -hsl-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/hsl-regex/-/hsl-regex-1.0.0.tgz#d49330c789ed819e276a4c0d272dffa30b18fe6e" - integrity sha1-1JMwx4ntgZ4nakwNJy3/owsY/m4= - -hsla-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/hsla-regex/-/hsla-regex-1.0.0.tgz#c1ce7a3168c8c6614033a4b5f7877f3b225f9c38" - integrity sha1-wc56MWjIxmFAM6S194d/OyJfnDg= - -html-comment-regex@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.2.tgz#97d4688aeb5c81886a364faa0cad1dda14d433a7" - integrity sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ== - -html-entities@^1.3.1: - version "1.4.0" - resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-1.4.0.tgz#cfbd1b01d2afaf9adca1b10ae7dffab98c71d2dc" - integrity sha512-8nxjcBcd8wovbeKx7h3wTji4e6+rhaVuPNpMqwWgnHh+N9ToqsCs6XztWRBPQ+UtzsoMAdKZtUENoVzU/EMtZA== - -html-escaper@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" - integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== - -http-cache-semantics@^3.8.1: - version "3.8.1" - resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz#39b0e16add9b605bf0a9ef3d9daaf4843b4cacd2" - integrity sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w== - -http-cache-semantics@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" - integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== - -http-deceiver@^1.2.7: - version "1.2.7" - resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" - integrity sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc= - -http-errors@1.7.2: - version "1.7.2" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.2.tgz#4f5029cf13239f31036e5b2e55292bcfbcc85c8f" - integrity sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg== - dependencies: - depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.1" - statuses ">= 1.5.0 < 2" - toidentifier "1.0.0" - -http-errors@~1.6.2: - version "1.6.3" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.6.3.tgz#8b55680bb4be283a0b5bf4ea2e38580be1d9320d" - integrity sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0= - dependencies: - depd "~1.1.2" - inherits "2.0.3" - setprototypeof "1.1.0" - statuses ">= 1.4.0 < 2" - -http-errors@~1.7.2: - version "1.7.3" - resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.7.3.tgz#6c619e4f9c60308c38519498c14fbb10aacebb06" - integrity sha512-ZTTX0MWrsQ2ZAhA1cejAwDLycFsd7I7nVtnkT3Ol0aqodaKW+0CTZDQ1uBv5whptCnc8e8HeRRJxRs0kmm/Qfw== - dependencies: - depd "~1.1.2" - inherits "2.0.4" - setprototypeof "1.1.1" - statuses ">= 1.5.0 < 2" - toidentifier "1.0.0" - -http-parser-js@>=0.5.1: - version "0.5.3" - resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.3.tgz#01d2709c79d41698bb01d4decc5e9da4e4a033d9" - integrity sha512-t7hjvef/5HEK7RWTdUzVUhl8zkEu+LlaE0IYzdMuvbSDipxBRpOn4Uhw8ZyECEa808iVT8XCjzo6xmYt4CiLZg== - -http-proxy-agent@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405" - integrity sha512-qwHbBLV7WviBl0rQsOzH6o5lwyOIvwp/BdFnvVxXORldu5TmjFfjzBcWUWS5kWAZhmv+JtiDhSuQCp4sBfbIgg== - dependencies: - agent-base "4" - debug "3.1.0" - -http-proxy-middleware@0.19.1: - version "0.19.1" - resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz#183c7dc4aa1479150306498c210cdaf96080a43a" - integrity sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q== - dependencies: - http-proxy "^1.17.0" - is-glob "^4.0.0" - lodash "^4.17.11" - micromatch "^3.1.10" - -http-proxy@^1.17.0, http-proxy@^1.18.1: - version "1.18.1" - resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" - integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== - dependencies: - eventemitter3 "^4.0.0" - follow-redirects "^1.0.0" - requires-port "^1.0.0" - -http-signature@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" - integrity sha1-muzZJRFHcvPZW2WmCruPfBj7rOE= - dependencies: - assert-plus "^1.0.0" - jsprim "^1.2.2" - sshpk "^1.7.0" - -https-browserify@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73" - integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM= - -https-proxy-agent@^2.2.1, https-proxy-agent@^2.2.3: - version "2.2.4" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-2.2.4.tgz#4ee7a737abd92678a293d9b34a1af4d0d08c787b" - integrity sha512-OmvfoQ53WLjtA9HeYP9RNrWMJzzAz1JGaSFr1nijg0PVR1JaD/xbJq1mdEIIlxGpXp9eSe/O2LgU9DJmTPd0Eg== - dependencies: - agent-base "^4.3.0" - debug "^3.1.0" - -humanize-ms@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" - integrity sha1-xG4xWaKT9riW2ikxbYtv6Lt5u+0= - dependencies: - ms "^2.0.0" - -iconv-lite@0.4.24, iconv-lite@^0.4.24: - version "0.4.24" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" - integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== - dependencies: - safer-buffer ">= 2.1.2 < 3" - -iconv-lite@^0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.2.tgz#ce13d1875b0c3a674bd6a04b7f76b01b1b6ded01" - integrity sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ== - dependencies: - safer-buffer ">= 2.1.2 < 3.0.0" - -icss-utils@^4.0.0, icss-utils@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-4.1.1.tgz#21170b53789ee27447c2f47dd683081403f9a467" - integrity sha512-4aFq7wvWyMHKgxsH8QQtGpvbASCf+eM3wPRLI6R+MgAnTCZ6STYsRvttLvRWK0Nfif5piF394St3HeJDaljGPA== - dependencies: - postcss "^7.0.14" - -ieee754@^1.1.4: - version "1.2.1" - resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" - integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== - -iferr@^0.1.5: - version "0.1.5" - resolved "https://registry.yarnpkg.com/iferr/-/iferr-0.1.5.tgz#c60eed69e6d8fdb6b3104a1fcbca1c192dc5b501" - integrity sha1-xg7taebY/bazEEofy8ocGS3FtQE= - -ignore-walk@^3.0.1: - version "3.0.3" - resolved "https://registry.yarnpkg.com/ignore-walk/-/ignore-walk-3.0.3.tgz#017e2447184bfeade7c238e4aefdd1e8f95b1e37" - integrity sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw== - dependencies: - minimatch "^3.0.4" - -ignore@^5.1.4: - version "5.1.8" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" - integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== - -image-size@~0.5.0: - version "0.5.5" - resolved "https://registry.yarnpkg.com/image-size/-/image-size-0.5.5.tgz#09dfd4ab9d20e29eb1c3e80b8990378df9e3cb9c" - integrity sha1-Cd/Uq50g4p6xw+gLiZA3jfnjy5w= - -immediate@~3.0.5: - version "3.0.6" - resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b" - integrity sha1-nbHb0Pr43m++D13V5Wu2BigN5ps= - -import-fresh@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-2.0.0.tgz#d81355c15612d386c61f9ddd3922d4304822a546" - integrity sha1-2BNVwVYS04bGH53dOSLUMEgipUY= - dependencies: - caller-path "^2.0.0" - resolve-from "^3.0.0" - -import-fresh@^3.2.1: - version "3.3.0" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" - integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== - dependencies: - parent-module "^1.0.0" - resolve-from "^4.0.0" - -import-local@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/import-local/-/import-local-2.0.0.tgz#55070be38a5993cf18ef6db7e961f5bee5c5a09d" - integrity sha512-b6s04m3O+s3CGSbqDIyP4R6aAwAeYlVq9+WUWep6iHa8ETRf9yei1U48C5MmfJmV9AiLYYBKPMq/W+/WRpQmCQ== - dependencies: - pkg-dir "^3.0.0" - resolve-cwd "^2.0.0" - -imurmurhash@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" - integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= - -indent-string@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" - integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== - -indexes-of@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/indexes-of/-/indexes-of-1.0.1.tgz#f30f716c8e2bd346c7b67d3df3915566a7c05607" - integrity sha1-8w9xbI4r00bHtn0985FVZqfAVgc= - -indexof@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/indexof/-/indexof-0.0.1.tgz#82dc336d232b9062179d05ab3293a66059fd435d" - integrity sha1-gtwzbSMrkGIXnQWrMpOmYFn9Q10= - -infer-owner@^1.0.3, infer-owner@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" - integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -inherits@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.1.tgz#b17d08d326b4423e568eff719f91b0b1cbdf69f1" - integrity sha1-sX0I0ya0Qj5Wjv9xn5GwscvfafE= - -inherits@2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= - -ini@1.3.6: - version "1.3.6" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.6.tgz#f1c46a2a93a253e7b3905115e74d527cd23061a1" - integrity sha512-IZUoxEjNjubzrmvzZU4lKP7OnYmX72XRl3sqkfJhBKweKi5rnGi5+IUdlj/H1M+Ip5JQ1WzaDMOBRY90Ajc5jg== - -ini@^1.3.4: - version "1.3.8" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" - integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== - -inquirer@7.3.3: - version "7.3.3" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-7.3.3.tgz#04d176b2af04afc157a83fd7c100e98ee0aad003" - integrity sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA== - dependencies: - ansi-escapes "^4.2.1" - chalk "^4.1.0" - cli-cursor "^3.1.0" - cli-width "^3.0.0" - external-editor "^3.0.3" - figures "^3.0.0" - lodash "^4.17.19" - mute-stream "0.0.8" - run-async "^2.4.0" - rxjs "^6.6.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - through "^2.3.6" - -internal-ip@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-4.3.0.tgz#845452baad9d2ca3b69c635a137acb9a0dad0907" - integrity sha512-S1zBo1D6zcsyuC6PMmY5+55YMILQ9av8lotMx447Bq6SAgo/sDK6y6uUKmuYhW7eacnIhFfsPmCNYdDzsnnDCg== - dependencies: - default-gateway "^4.2.0" - ipaddr.js "^1.9.0" - -ip-regex@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" - integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk= - -ip@1.1.5, ip@^1.1.0, ip@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" - integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= - -ipaddr.js@1.9.1, ipaddr.js@^1.9.0: - version "1.9.1" - resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3" - integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g== - -is-absolute-url@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6" - integrity sha1-UFMN+4T8yap9vnhS6Do3uTufKqY= - -is-absolute-url@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-3.0.3.tgz#96c6a22b6a23929b11ea0afb1836c36ad4a5d698" - integrity sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q== - -is-accessor-descriptor@^0.1.6: - version "0.1.6" - resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" - integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= - dependencies: - kind-of "^3.0.2" - -is-accessor-descriptor@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" - integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== - dependencies: - kind-of "^6.0.0" - -is-arguments@^1.0.4: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-arguments/-/is-arguments-1.1.0.tgz#62353031dfbee07ceb34656a6bde59efecae8dd9" - integrity sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg== - dependencies: - call-bind "^1.0.0" - -is-arrayish@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" - integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= - -is-arrayish@^0.3.1: - version "0.3.2" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" - integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== - -is-bigint@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.1.tgz#6923051dfcbc764278540b9ce0e6b3213aa5ebc2" - integrity sha512-J0ELF4yHFxHy0cmSxZuheDOz2luOdVvqjwmEcj8H/L1JHeuEDSDbeRP+Dk9kFVk5RTFzbucJ2Kb9F7ixY2QaCg== - -is-binary-path@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" - integrity sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg= - dependencies: - binary-extensions "^1.0.0" - -is-binary-path@~2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" - integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== - dependencies: - binary-extensions "^2.0.0" - -is-boolean-object@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.0.tgz#e2aaad3a3a8fca34c28f6eee135b156ed2587ff0" - integrity sha512-a7Uprx8UtD+HWdyYwnD1+ExtTgqQtD2k/1yJgtXP6wnMm8byhkoTZRl+95LLThpzNZJ5aEvi46cdH+ayMFRwmA== - dependencies: - call-bind "^1.0.0" - -is-buffer@^1.1.5: - version "1.1.6" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" - integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== - -is-callable@^1.1.4, is-callable@^1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.3.tgz#8b1e0500b73a1d76c70487636f368e519de8db8e" - integrity sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ== - -is-color-stop@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-color-stop/-/is-color-stop-1.1.0.tgz#cfff471aee4dd5c9e158598fbe12967b5cdad345" - integrity sha1-z/9HGu5N1cnhWFmPvhKWe1za00U= - dependencies: - css-color-names "^0.0.4" - hex-color-regex "^1.1.0" - hsl-regex "^1.0.0" - hsla-regex "^1.0.0" - rgb-regex "^1.0.1" - rgba-regex "^1.0.0" - -is-core-module@^2.0.0, is-core-module@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.2.0.tgz#97037ef3d52224d85163f5597b2b63d9afed981a" - integrity sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ== - dependencies: - has "^1.0.3" - -is-data-descriptor@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" - integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= - dependencies: - kind-of "^3.0.2" - -is-data-descriptor@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" - integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== - dependencies: - kind-of "^6.0.0" - -is-date-object@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" - integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== - -is-descriptor@^0.1.0: - version "0.1.6" - resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" - integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== - dependencies: - is-accessor-descriptor "^0.1.6" - is-data-descriptor "^0.1.4" - kind-of "^5.0.0" - -is-descriptor@^1.0.0, is-descriptor@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" - integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== - dependencies: - is-accessor-descriptor "^1.0.0" - is-data-descriptor "^1.0.0" - kind-of "^6.0.2" - -is-directory@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/is-directory/-/is-directory-0.3.1.tgz#61339b6f2475fc772fd9c9d83f5c8575dc154ae1" - integrity sha1-YTObbyR1/Hcv2cnYP1yFddwVSuE= - -is-docker@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.1.1.tgz#4125a88e44e450d384e09047ede71adc2d144156" - integrity sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw== - -is-extendable@^0.1.0, is-extendable@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" - integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= - -is-extendable@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" - integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== - dependencies: - is-plain-object "^2.0.4" - -is-extglob@^2.1.0, is-extglob@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= - -is-fullwidth-code-point@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" - integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= - -is-fullwidth-code-point@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" - integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== - -is-glob@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" - integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo= - dependencies: - is-extglob "^2.1.0" - -is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" - integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== - dependencies: - is-extglob "^2.1.1" - -is-interactive@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" - integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== - -is-negative-zero@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" - integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w== - -is-number-object@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.4.tgz#36ac95e741cf18b283fc1ddf5e83da798e3ec197" - integrity sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw== - -is-number@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" - integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= - dependencies: - kind-of "^3.0.2" - -is-number@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" - integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== - -is-obj@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" - integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== - -is-path-cwd@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" - integrity sha1-0iXsIxMuie3Tj9p2dHLmLmXxEG0= - -is-path-cwd@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-2.2.0.tgz#67d43b82664a7b5191fd9119127eb300048a9fdb" - integrity sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ== - -is-path-in-cwd@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-1.0.1.tgz#5ac48b345ef675339bd6c7a48a912110b241cf52" - integrity sha512-FjV1RTW48E7CWM7eE/J2NJvAEEVektecDBVBE5Hh3nM1Jd0kvhHtX68Pr3xsDf857xt3Y4AkwVULK1Vku62aaQ== - dependencies: - is-path-inside "^1.0.0" - -is-path-in-cwd@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-path-in-cwd/-/is-path-in-cwd-2.1.0.tgz#bfe2dca26c69f397265a4009963602935a053acb" - integrity sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ== - dependencies: - is-path-inside "^2.1.0" - -is-path-inside@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-1.0.1.tgz#8ef5b7de50437a3fdca6b4e865ef7aa55cb48036" - integrity sha1-jvW33lBDej/cprToZe96pVy0gDY= - dependencies: - path-is-inside "^1.0.1" - -is-path-inside@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-2.1.0.tgz#7c9810587d659a40d27bcdb4d5616eab059494b2" - integrity sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg== - dependencies: - path-is-inside "^1.0.2" - -is-plain-object@^2.0.3, is-plain-object@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" - integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== - dependencies: - isobject "^3.0.1" - -is-regex@^1.0.4, is-regex@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.2.tgz#81c8ebde4db142f2cf1c53fc86d6a45788266251" - integrity sha512-axvdhb5pdhEVThqJzYXwMlVuZwC+FF2DpcOhTS+y/8jVq4trxyPgfcwIxIKiyeuLlSQYKkmUaPQJ8ZE4yNKXDg== - dependencies: - call-bind "^1.0.2" - has-symbols "^1.0.1" - -is-resolvable@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" - integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg== - -is-stream@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44" - integrity sha1-EtSj3U5o4Lec6428hBc66A2RykQ= - -is-string@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" - integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ== - -is-svg@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-svg/-/is-svg-3.0.0.tgz#9321dbd29c212e5ca99c4fa9794c714bcafa2f75" - integrity sha512-gi4iHK53LR2ujhLVVj+37Ykh9GLqYHX6JOVXbLAucaG/Cqw9xwdFOjDM2qeifLs1sF1npXXFvDu0r5HNgCMrzQ== - dependencies: - html-comment-regex "^1.1.0" - -is-symbol@^1.0.2, is-symbol@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" - integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ== - dependencies: - has-symbols "^1.0.1" - -is-typedarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= - -is-windows@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" - integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== - -is-wsl@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" - integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= - -is-wsl@^2.1.1: - version "2.2.0" - resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" - integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== - dependencies: - is-docker "^2.0.0" - -isarray@1.0.0, isarray@^1.0.0, isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= - -isarray@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.1.tgz#a37d94ed9cda2d59865c9f76fe596ee1f338741e" - integrity sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4= - -isbinaryfile@^4.0.6: - version "4.0.6" - resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-4.0.6.tgz#edcb62b224e2b4710830b67498c8e4e5a4d2610b" - integrity sha512-ORrEy+SNVqUhrCaal4hA4fBzhggQQ+BaLntyPOdoEiwlKZW9BZiJXjg3RMiruE4tPEI3pyVPpySHQF/dKWperg== - -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= - -isobject@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" - integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= - dependencies: - isarray "1.0.0" - -isobject@^3.0.0, isobject@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" - integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= - -isstream@~0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" - integrity sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo= - -istanbul-lib-coverage@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.5.tgz#675f0ab69503fad4b1d849f736baaca803344f49" - integrity sha512-8aXznuEPCJvGnMSRft4udDRDtb1V3pkQkMMI5LI+6HuQz5oQ4J2UFn1H82raA3qJtyOLkkwVqICBQkjnGtn5mA== - -istanbul-lib-coverage@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz#f5944a37c70b550b02a78a5c3b2055b280cec8ec" - integrity sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg== - -istanbul-lib-instrument@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz#873c6fff897450118222774696a3f28902d77c1d" - integrity sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ== - dependencies: - "@babel/core" "^7.7.5" - "@istanbuljs/schema" "^0.1.2" - istanbul-lib-coverage "^3.0.0" - semver "^6.3.0" - -istanbul-lib-report@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" - integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== - dependencies: - istanbul-lib-coverage "^3.0.0" - make-dir "^3.0.0" - supports-color "^7.1.0" - -istanbul-lib-source-maps@^3.0.6: - version "3.0.6" - resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-3.0.6.tgz#284997c48211752ec486253da97e3879defba8c8" - integrity sha512-R47KzMtDJH6X4/YW9XTx+jrLnZnscW4VpNN+1PViSYTejLVPWv7oov+Duf8YQSPyVRUvueQqz1TcsC6mooZTXw== - dependencies: - debug "^4.1.1" - istanbul-lib-coverage "^2.0.5" - make-dir "^2.1.0" - rimraf "^2.6.3" - source-map "^0.6.1" - -istanbul-reports@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.0.2.tgz#d593210e5000683750cb09fc0644e4b6e27fd53b" - integrity sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw== - dependencies: - html-escaper "^2.0.0" - istanbul-lib-report "^3.0.0" - -jasmine-core@^3.6.0, jasmine-core@~3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-3.6.0.tgz#491f3bb23941799c353ceb7a45b38a950ebc5a20" - integrity sha512-8uQYa7zJN8hq9z+g8z1bqCfdC8eoDAeVnM5sfqs7KHv9/ifoJ500m018fpFc7RDaO6SWCLCXwo/wPSNcdYTgcw== - -jasmine-core@~2.8.0: - version "2.8.0" - resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-2.8.0.tgz#bcc979ae1f9fd05701e45e52e65d3a5d63f1a24e" - integrity sha1-vMl5rh+f0FcB5F5S5l06XWPxok4= - -jasmine-spec-reporter@~5.0.0: - version "5.0.2" - resolved "https://registry.yarnpkg.com/jasmine-spec-reporter/-/jasmine-spec-reporter-5.0.2.tgz#b61288ab074ad440dc2477c4d42840b0e74a6b95" - integrity sha512-6gP1LbVgJ+d7PKksQBc2H0oDGNRQI3gKUsWlswKaQ2fif9X5gzhQcgM5+kiJGCQVurOG09jqNhk7payggyp5+g== - dependencies: - colors "1.4.0" - -jasmine@2.8.0: - version "2.8.0" - resolved "https://registry.yarnpkg.com/jasmine/-/jasmine-2.8.0.tgz#6b089c0a11576b1f16df11b80146d91d4e8b8a3e" - integrity sha1-awicChFXax8W3xG4AUbZHU6Lij4= - dependencies: - exit "^0.1.2" - glob "^7.0.6" - jasmine-core "~2.8.0" - -jasminewd2@^2.1.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/jasminewd2/-/jasminewd2-2.2.0.tgz#e37cf0b17f199cce23bea71b2039395246b4ec4e" - integrity sha1-43zwsX8ZnM4jvqcbIDk5Uka07E4= - -jest-worker@26.5.0: - version "26.5.0" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.5.0.tgz#87deee86dbbc5f98d9919e0dadf2c40e3152fa30" - integrity sha512-kTw66Dn4ZX7WpjZ7T/SUDgRhapFRKWmisVAF0Rv4Fu8SLFD7eLbqpLvbxVqYhSgaWa7I+bW7pHnbyfNsH6stug== - dependencies: - "@types/node" "*" - merge-stream "^2.0.0" - supports-color "^7.0.0" - -jest-worker@^26.5.0: - version "26.6.2" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" - integrity sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ== - dependencies: - "@types/node" "*" - merge-stream "^2.0.0" - supports-color "^7.0.0" - -js-tokens@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" - integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== - -js-yaml@^3.13.1: - version "3.14.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" - integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - -jsbn@~0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" - integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= - -jsesc@^2.5.1: - version "2.5.2" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" - integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== - -jsesc@~0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d" - integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0= - -json-buffer@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898" - integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= - -json-parse-better-errors@^1.0.0, json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" - integrity sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw== - -json-parse-even-better-errors@^2.3.0: - version "2.3.1" - resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" - integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== - -json-schema-traverse@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" - integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== - -json-schema@0.2.3: - version "0.2.3" - resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" - integrity sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM= - -json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" - integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= - -json3@^3.3.2: - version "3.3.3" - resolved "https://registry.yarnpkg.com/json3/-/json3-3.3.3.tgz#7fc10e375fc5ae42c4705a5cc0aa6f62be305b81" - integrity sha512-c7/8mbUsKigAbLkD5B010BK4D9LZm7A1pNItkEwiUZRpIN66exu/e7YQWysGun+TRKaJp8MhemM+VkfWv42aCA== - -json5@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" - integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== - dependencies: - minimist "^1.2.0" - -json5@^2.1.2: - version "2.2.0" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz#2dfefe720c6ba525d9ebd909950f0515316c89a3" - integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA== - dependencies: - minimist "^1.2.5" - -jsonc-parser@2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.3.1.tgz#59549150b133f2efacca48fe9ce1ec0659af2342" - integrity sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg== - -jsonfile@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" - integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= - optionalDependencies: - graceful-fs "^4.1.6" - -jsonparse@^1.2.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" - integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA= - -jsprim@^1.2.2: - version "1.4.1" - resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" - integrity sha1-MT5mvB5cwG5Di8G3SZwuXFastqI= - dependencies: - assert-plus "1.0.0" - extsprintf "1.3.0" - json-schema "0.2.3" - verror "1.10.0" - -jszip@^3.1.3: - version "3.6.0" - resolved "https://registry.yarnpkg.com/jszip/-/jszip-3.6.0.tgz#839b72812e3f97819cc13ac4134ffced95dd6af9" - integrity sha512-jgnQoG9LKnWO3mnVNBnfhkh0QknICd1FGSrXcgrl67zioyJ4wgx25o9ZqwNtrROSflGBCGYnJfjrIyRIby1OoQ== - dependencies: - lie "~3.3.0" - pako "~1.0.2" - readable-stream "~2.3.6" - set-immediate-shim "~1.0.1" - -karma-chrome-launcher@~3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/karma-chrome-launcher/-/karma-chrome-launcher-3.1.0.tgz#805a586799a4d05f4e54f72a204979f3f3066738" - integrity sha512-3dPs/n7vgz1rxxtynpzZTvb9y/GIaW8xjAwcIGttLbycqoFtI7yo1NGnQi6oFTherRE+GIhCAHZC4vEqWGhNvg== - dependencies: - which "^1.2.1" - -karma-coverage-istanbul-reporter@~3.0.2: - version "3.0.3" - resolved "https://registry.yarnpkg.com/karma-coverage-istanbul-reporter/-/karma-coverage-istanbul-reporter-3.0.3.tgz#f3b5303553aadc8e681d40d360dfdc19bc7e9fe9" - integrity sha512-wE4VFhG/QZv2Y4CdAYWDbMmcAHeS926ZIji4z+FkB2aF/EposRb6DP6G5ncT/wXhqUfAb/d7kZrNKPonbvsATw== - dependencies: - istanbul-lib-coverage "^3.0.0" - istanbul-lib-report "^3.0.0" - istanbul-lib-source-maps "^3.0.6" - istanbul-reports "^3.0.2" - minimatch "^3.0.4" - -karma-jasmine-html-reporter@^1.5.0: - version "1.5.4" - resolved "https://registry.yarnpkg.com/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-1.5.4.tgz#669f33d694d88fce1b0ccfda57111de716cb0192" - integrity sha512-PtilRLno5O6wH3lDihRnz0Ba8oSn0YUJqKjjux1peoYGwo0AQqrWRbdWk/RLzcGlb+onTyXAnHl6M+Hu3UxG/Q== - -karma-jasmine@~4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/karma-jasmine/-/karma-jasmine-4.0.1.tgz#b99e073b6d99a5196fc4bffc121b89313b0abd82" - integrity sha512-h8XDAhTiZjJKzfkoO1laMH+zfNlra+dEQHUAjpn5JV1zCPtOIVWGQjLBrqhnzQa/hrU2XrZwSyBa6XjEBzfXzw== - dependencies: - jasmine-core "^3.6.0" - -karma-source-map-support@1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/karma-source-map-support/-/karma-source-map-support-1.4.0.tgz#58526ceccf7e8730e56effd97a4de8d712ac0d6b" - integrity sha512-RsBECncGO17KAoJCYXjv+ckIz+Ii9NCi+9enk+rq6XC81ezYkb4/RHE6CTXdA7IOJqoF3wcaLfVG0CPmE5ca6A== - dependencies: - source-map-support "^0.5.5" - -karma@~5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/karma/-/karma-5.1.1.tgz#4e472c1e5352d73edbd2090726afdb01d7869d72" - integrity sha512-xAlOr5PMqUbiKXSv5PCniHWV3aiwj6wIZ0gUVcwpTCPVQm/qH2WAMFWxtnpM6KJqhkRWrIpovR4Rb0rn8GtJzQ== - dependencies: - body-parser "^1.19.0" - braces "^3.0.2" - chokidar "^3.0.0" - colors "^1.4.0" - connect "^3.7.0" - di "^0.0.1" - dom-serialize "^2.2.1" - flatted "^2.0.2" - glob "^7.1.6" - graceful-fs "^4.2.4" - http-proxy "^1.18.1" - isbinaryfile "^4.0.6" - lodash "^4.17.15" - log4js "^6.2.1" - mime "^2.4.5" - minimatch "^3.0.4" - qjobs "^1.2.0" - range-parser "^1.2.1" - rimraf "^3.0.2" - socket.io "^2.3.0" - source-map "^0.6.1" - tmp "0.2.1" - ua-parser-js "0.7.21" - yargs "^15.3.1" - -keyv@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz#ecc228486f69991e49e9476485a5be1e8fc5c4d9" - integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA== - dependencies: - json-buffer "3.0.0" - -killable@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892" - integrity sha512-LzqtLKlUwirEUyl/nicirVmNiPvYs7l5n8wOPP7fyJVpUPkvCnW/vuiXGpylGUlnPDnB7311rARzAt3Mhswpjg== - -kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: - version "3.2.2" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" - integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= - dependencies: - is-buffer "^1.1.5" - -kind-of@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" - integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= - dependencies: - is-buffer "^1.1.5" - -kind-of@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" - integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== - -kind-of@^6.0.0, kind-of@^6.0.2: - version "6.0.3" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" - integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== - -klona@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.4.tgz#7bb1e3affb0cb8624547ef7e8f6708ea2e39dfc0" - integrity sha512-ZRbnvdg/NxqzC7L9Uyqzf4psi1OM4Cuc+sJAkQPjO6XkQIJTNbfK2Rsmbw8fx1p2mkZdp2FZYo2+LwXYY/uwIA== - -less-loader@7.0.2: - version "7.0.2" - resolved "https://registry.yarnpkg.com/less-loader/-/less-loader-7.0.2.tgz#0d73a49ec32a9d3ff12614598e6e2b47fb2a35c4" - integrity sha512-7MKlgjnkCf63E3Lv6w2FvAEgLMx3d/tNBExITcanAq7ys5U8VPWT3F6xcRjYmdNfkoQ9udoVFb1r2azSiTnD6w== - dependencies: - klona "^2.0.4" - loader-utils "^2.0.0" - schema-utils "^3.0.0" - -less@3.12.2: - version "3.12.2" - resolved "https://registry.yarnpkg.com/less/-/less-3.12.2.tgz#157e6dd32a68869df8859314ad38e70211af3ab4" - integrity sha512-+1V2PCMFkL+OIj2/HrtrvZw0BC0sYLMICJfbQjuj/K8CEnlrFX6R5cKKgzzttsZDHyxQNL1jqMREjKN3ja/E3Q== - dependencies: - tslib "^1.10.0" - optionalDependencies: - errno "^0.1.1" - graceful-fs "^4.1.2" - image-size "~0.5.0" - make-dir "^2.1.0" - mime "^1.4.1" - native-request "^1.0.5" - source-map "~0.6.0" - -license-webpack-plugin@2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/license-webpack-plugin/-/license-webpack-plugin-2.3.1.tgz#08eddb2f776c7c64c02f308a00e017d6e824d0b6" - integrity sha512-yhqTmlYIEpZWA122lf6E0G8+rkn0AzoQ1OpzUKKs/lXUqG1plmGnwmkuuPlfggzJR5y6DLOdot/Tv00CC51CeQ== - dependencies: - "@types/webpack-sources" "^0.1.5" - webpack-sources "^1.2.0" - -lie@~3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/lie/-/lie-3.3.0.tgz#dcf82dee545f46074daf200c7c1c5a08e0f40f6a" - integrity sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ== - dependencies: - immediate "~3.0.5" - -lines-and-columns@^1.1.6: - version "1.1.6" - resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" - integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= - -loader-runner@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357" - integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw== - -loader-utils@1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7" - integrity sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA== - dependencies: - big.js "^5.2.2" - emojis-list "^2.0.0" - json5 "^1.0.1" - -loader-utils@2.0.0, loader-utils@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-2.0.0.tgz#e4cace5b816d425a166b5f097e10cd12b36064b0" - integrity sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ== - dependencies: - big.js "^5.2.2" - emojis-list "^3.0.0" - json5 "^2.1.2" - -loader-utils@^1.1.0, loader-utils@^1.2.3, loader-utils@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613" - integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA== - dependencies: - big.js "^5.2.2" - emojis-list "^3.0.0" - json5 "^1.0.1" - -locate-path@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e" - integrity sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A== - dependencies: - p-locate "^3.0.0" - path-exists "^3.0.0" - -locate-path@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" - integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== - dependencies: - p-locate "^4.1.0" - -lodash.memoize@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" - integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= - -lodash.uniq@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773" - integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M= - -lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19: - version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== - -log-symbols@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.0.0.tgz#69b3cc46d20f448eccdb75ea1fa733d9e821c920" - integrity sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA== - dependencies: - chalk "^4.0.0" - -log4js@^6.2.1: - version "6.3.0" - resolved "https://registry.yarnpkg.com/log4js/-/log4js-6.3.0.tgz#10dfafbb434351a3e30277a00b9879446f715bcb" - integrity sha512-Mc8jNuSFImQUIateBFwdOQcmC6Q5maU0VVvdC2R6XMb66/VnT+7WS4D/0EeNMZu1YODmJe5NIn2XftCzEocUgw== - dependencies: - date-format "^3.0.0" - debug "^4.1.1" - flatted "^2.0.1" - rfdc "^1.1.4" - streamroller "^2.2.4" - -loglevel@^1.6.8: - version "1.7.1" - resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.7.1.tgz#005fde2f5e6e47068f935ff28573e125ef72f197" - integrity sha512-Hesni4s5UkWkwCGJMQGAh71PaLUmKFM60dHvq0zi/vDhhrzuk+4GgNbTXJ12YYQJn6ZKBDNIjYcuQGKudvqrIw== - -lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f" - integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== - -lowercase-keys@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" - integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== - -lru-cache@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" - integrity sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w== - dependencies: - yallist "^3.0.2" - -lru-cache@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" - integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== - dependencies: - yallist "^4.0.0" - -magic-string@0.25.7, magic-string@^0.25.0: - version "0.25.7" - resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.7.tgz#3f497d6fd34c669c6798dcb821f2ef31f5445051" - integrity sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA== - dependencies: - sourcemap-codec "^1.4.4" - -make-dir@^2.0.0, make-dir@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" - integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== - dependencies: - pify "^4.0.1" - semver "^5.6.0" - -make-dir@^3.0.0, make-dir@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" - integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== - dependencies: - semver "^6.0.0" - -make-error@^1.1.1: - version "1.3.6" - resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" - integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== - -make-fetch-happen@^5.0.0: - version "5.0.2" - resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-5.0.2.tgz#aa8387104f2687edca01c8687ee45013d02d19bd" - integrity sha512-07JHC0r1ykIoruKO8ifMXu+xEU8qOXDFETylktdug6vJDACnP+HKevOu3PXyNPzFyTSlz8vrBYlBO1JZRe8Cag== - dependencies: - agentkeepalive "^3.4.1" - cacache "^12.0.0" - http-cache-semantics "^3.8.1" - http-proxy-agent "^2.1.0" - https-proxy-agent "^2.2.3" - lru-cache "^5.1.1" - mississippi "^3.0.0" - node-fetch-npm "^2.0.2" - promise-retry "^1.1.1" - socks-proxy-agent "^4.0.0" - ssri "^6.0.0" - -map-cache@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" - integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= - -map-visit@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" - integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= - dependencies: - object-visit "^1.0.0" - -matcher@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/matcher/-/matcher-3.0.0.tgz#bd9060f4c5b70aa8041ccc6f80368760994f30ca" - integrity sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng== - dependencies: - escape-string-regexp "^4.0.0" - -md5.js@^1.3.4: - version "1.3.5" - resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f" - integrity sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg== - dependencies: - hash-base "^3.0.0" - inherits "^2.0.1" - safe-buffer "^5.1.2" - -mdn-data@2.0.14: - version "2.0.14" - resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" - integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== - -mdn-data@2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.4.tgz#699b3c38ac6f1d728091a64650b65d388502fd5b" - integrity sha512-iV3XNKw06j5Q7mi6h+9vbx23Tv7JkjEVgKHW4pimwyDGWm0OIQntJJ+u1C6mg6mK1EaTv42XQ7w76yuzH7M2cA== - -media-typer@0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" - integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= - -memory-fs@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.4.1.tgz#3a9a20b8462523e447cfbc7e8bb80ed667bfc552" - integrity sha1-OpoguEYlI+RHz7x+i7gO1me/xVI= - dependencies: - errno "^0.1.3" - readable-stream "^2.0.1" - -memory-fs@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/memory-fs/-/memory-fs-0.5.0.tgz#324c01288b88652966d161db77838720845a8e3c" - integrity sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA== - dependencies: - errno "^0.1.3" - readable-stream "^2.0.1" - -merge-descriptors@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" - integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E= - -merge-source-map@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/merge-source-map/-/merge-source-map-1.1.0.tgz#2fdde7e6020939f70906a68f2d7ae685e4c8c646" - integrity sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw== - dependencies: - source-map "^0.6.1" - -merge-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" - integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== - -merge2@^1.3.0: - version "1.4.1" - resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" - integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== - -methods@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" - integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= - -micromatch@^3.1.10, micromatch@^3.1.4: - version "3.1.10" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" - integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== - dependencies: - arr-diff "^4.0.0" - array-unique "^0.3.2" - braces "^2.3.1" - define-property "^2.0.2" - extend-shallow "^3.0.2" - extglob "^2.0.4" - fragment-cache "^0.2.1" - kind-of "^6.0.2" - nanomatch "^1.2.9" - object.pick "^1.3.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.2" - -micromatch@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.2.tgz#4fcb0999bf9fbc2fcbdd212f6d629b9a56c39259" - integrity sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q== - dependencies: - braces "^3.0.1" - picomatch "^2.0.5" - -miller-rabin@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d" - integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA== - dependencies: - bn.js "^4.0.0" - brorand "^1.0.1" - -mime-db@1.46.0, "mime-db@>= 1.43.0 < 2": - version "1.46.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.46.0.tgz#6267748a7f799594de3cbc8cde91def349661cee" - integrity sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ== - -mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24: - version "2.1.29" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.29.tgz#1d4ab77da64b91f5f72489df29236563754bb1b2" - integrity sha512-Y/jMt/S5sR9OaqteJtslsFZKWOIIqMACsJSiHghlCAyhf7jfVYjKBmLiX8OgpWeW+fjJ2b+Az69aPFPkUOY6xQ== - dependencies: - mime-db "1.46.0" - -mime@1.6.0, mime@^1.4.1: - version "1.6.0" - resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" - integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== - -mime@^2.4.4, mime@^2.4.5: - version "2.5.2" - resolved "https://registry.yarnpkg.com/mime/-/mime-2.5.2.tgz#6e3dc6cc2b9510643830e5f19d5cb753da5eeabe" - integrity sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg== - -mimic-fn@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" - integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== - -mimic-response@^1.0.0, mimic-response@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b" - integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== - -mini-css-extract-plugin@1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/mini-css-extract-plugin/-/mini-css-extract-plugin-1.2.1.tgz#30ea7dee632b3002b0c77aeed447790408cb247e" - integrity sha512-G3yw7/TQaPfkuiR73MDcyiqhyP8SnbmLhUbpC76H+wtQxA6wfKhMCQOCb6wnPK0dQbjORAeOILQqEesg4/wF7A== - dependencies: - loader-utils "^2.0.0" - schema-utils "^3.0.0" - webpack-sources "^1.1.0" - -minimalistic-assert@^1.0.0, minimalistic-assert@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz#2e194de044626d4a10e7f7fbc00ce73e83e4d5c7" - integrity sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A== - -minimalistic-crypto-utils@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz#f6c00c1c0b082246e5c4d99dfb8c7c083b2b582a" - integrity sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo= - -minimatch@3.0.4, minimatch@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== - dependencies: - brace-expansion "^1.1.7" - -minimist@^1.2.0, minimist@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" - integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== - -minipass-collect@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617" - integrity sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA== - dependencies: - minipass "^3.0.0" - -minipass-flush@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/minipass-flush/-/minipass-flush-1.0.5.tgz#82e7135d7e89a50ffe64610a787953c4c4cbb373" - integrity sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw== - dependencies: - minipass "^3.0.0" - -minipass-pipeline@^1.2.2: - version "1.2.4" - resolved "https://registry.yarnpkg.com/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz#68472f79711c084657c067c5c6ad93cddea8214c" - integrity sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A== - dependencies: - minipass "^3.0.0" - -minipass@^2.3.5, minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0: - version "2.9.0" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6" - integrity sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg== - dependencies: - safe-buffer "^5.1.2" - yallist "^3.0.0" - -minipass@^3.0.0, minipass@^3.1.1: - version "3.1.3" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.1.3.tgz#7d42ff1f39635482e15f9cdb53184deebd5815fd" - integrity sha512-Mgd2GdMVzY+x3IJ+oHnVM+KG3lA5c8tnabyJKmHSaG2kAGpudxuOf8ToDkhumF7UzME7DecbQE9uOZhNm7PuJg== - dependencies: - yallist "^4.0.0" - -minizlib@^1.2.1: - version "1.3.3" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-1.3.3.tgz#2290de96818a34c29551c8a8d301216bd65a861d" - integrity sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q== - dependencies: - minipass "^2.9.0" - -minizlib@^2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" - integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg== - dependencies: - minipass "^3.0.0" - yallist "^4.0.0" - -mississippi@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/mississippi/-/mississippi-3.0.0.tgz#ea0a3291f97e0b5e8776b363d5f0a12d94c67022" - integrity sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA== - dependencies: - concat-stream "^1.5.0" - duplexify "^3.4.2" - end-of-stream "^1.1.0" - flush-write-stream "^1.0.0" - from2 "^2.1.0" - parallel-transform "^1.1.0" - pump "^3.0.0" - pumpify "^1.3.3" - stream-each "^1.1.0" - through2 "^2.0.0" - -mixin-deep@^1.2.0: - version "1.3.2" - resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" - integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== - dependencies: - for-in "^1.0.2" - is-extendable "^1.0.1" - -mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@^0.5.4, mkdirp@^0.5.5, mkdirp@~0.5.1: - version "0.5.5" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def" - integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== - dependencies: - minimist "^1.2.5" - -mkdirp@^1.0.3, mkdirp@^1.0.4, mkdirp@~1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" - integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== - -move-concurrently@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92" - integrity sha1-viwAX9oy4LKa8fBdfEszIUxwH5I= - dependencies: - aproba "^1.1.1" - copy-concurrently "^1.0.0" - fs-write-stream-atomic "^1.0.8" - mkdirp "^0.5.1" - rimraf "^2.5.4" - run-queue "^1.0.3" - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= - -ms@2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" - integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== - -ms@2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - -ms@^2.0.0, ms@^2.1.1: - version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - -multicast-dns-service-types@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz#899f11d9686e5e05cb91b35d5f0e63b773cfc901" - integrity sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE= - -multicast-dns@^6.0.1: - version "6.2.3" - resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-6.2.3.tgz#a0ec7bd9055c4282f790c3c82f4e28db3b31b229" - integrity sha512-ji6J5enbMyGRHIAkAOu3WdV8nggqviKCEKtXcOqfphZZtQrmHKycfynJ2V7eVPUA4NhJ6V7Wf4TmGbTwKE9B6g== - dependencies: - dns-packet "^1.3.1" - thunky "^1.0.2" - -mute-stream@0.0.8: - version "0.0.8" - resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" - integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== - -nan@^2.12.1: - version "2.14.2" - resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.2.tgz#f5376400695168f4cc694ac9393d0c9585eeea19" - integrity sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ== - -nanomatch@^1.2.9: - version "1.2.13" - resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" - integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== - dependencies: - arr-diff "^4.0.0" - array-unique "^0.3.2" - define-property "^2.0.2" - extend-shallow "^3.0.2" - fragment-cache "^0.2.1" - is-windows "^1.0.2" - kind-of "^6.0.2" - object.pick "^1.3.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - -native-request@^1.0.5: - version "1.0.8" - resolved "https://registry.yarnpkg.com/native-request/-/native-request-1.0.8.tgz#8f66bf606e0f7ea27c0e5995eb2f5d03e33ae6fb" - integrity sha512-vU2JojJVelUGp6jRcLwToPoWGxSx23z/0iX+I77J3Ht17rf2INGjrhOoQnjVo60nQd8wVsgzKkPfRXBiVdD2ag== - -negotiator@0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.2.tgz#feacf7ccf525a77ae9634436a64883ffeca346fb" - integrity sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw== - -neo-async@^2.5.0, neo-async@^2.6.1, neo-async@^2.6.2: - version "2.6.2" - resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" - integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== - -next-tick@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" - integrity sha1-yobR/ogoFpsBICCOPchCS524NCw= - -ngx-electron@2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/ngx-electron/-/ngx-electron-2.2.0.tgz#8ab65427476153b499d26f332fa5ca88714d8477" - integrity sha512-Yl7Dsnvp97k0XpIuiB54X7Ij2+zU5x0pCAYnN//VZ9tF7c6S3//OZ9dN9Et7p/zIjCV3Hg9vyw68dPJEZGk+LQ== - dependencies: - tslib "^1.9.0" - -nice-try@^1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.5.tgz#a3378a7696ce7d223e88fc9b764bd7ef1089e366" - integrity sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ== - -node-fetch-npm@^2.0.2: - version "2.0.4" - resolved "https://registry.yarnpkg.com/node-fetch-npm/-/node-fetch-npm-2.0.4.tgz#6507d0e17a9ec0be3bec516958a497cec54bf5a4" - integrity sha512-iOuIQDWDyjhv9qSDrj9aq/klt6F9z1p2otB3AV7v3zBDcL/x+OfGsvGQZZCcMZbUf4Ujw1xGNQkjvGnVT22cKg== - dependencies: - encoding "^0.1.11" - json-parse-better-errors "^1.0.0" - safe-buffer "^5.1.1" - -node-forge@^0.10.0: - version "0.10.0" - resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3" - integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA== - -node-libs-browser@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/node-libs-browser/-/node-libs-browser-2.2.1.tgz#b64f513d18338625f90346d27b0d235e631f6425" - integrity sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q== - dependencies: - assert "^1.1.1" - browserify-zlib "^0.2.0" - buffer "^4.3.0" - console-browserify "^1.1.0" - constants-browserify "^1.0.0" - crypto-browserify "^3.11.0" - domain-browser "^1.1.1" - events "^3.0.0" - https-browserify "^1.0.0" - os-browserify "^0.3.0" - path-browserify "0.0.1" - process "^0.11.10" - punycode "^1.2.4" - querystring-es3 "^0.2.0" - readable-stream "^2.3.3" - stream-browserify "^2.0.1" - stream-http "^2.7.2" - string_decoder "^1.0.0" - timers-browserify "^2.0.4" - tty-browserify "0.0.0" - url "^0.11.0" - util "^0.11.0" - vm-browserify "^1.0.1" - -node-releases@^1.1.70: - version "1.1.71" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.71.tgz#cb1334b179896b1c89ecfdd4b725fb7bbdfc7dbb" - integrity sha512-zR6HoT6LrLCRBwukmrVbHv0EpEQjksO6GmFcZQQuCAy139BEsoVKPYnf3jongYW83fAa1torLGYwxxky/p28sg== - -normalize-package-data@^2.4.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" - integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== - dependencies: - hosted-git-info "^2.1.4" - resolve "^1.10.0" - semver "2 || 3 || 4 || 5" - validate-npm-package-license "^3.0.1" - -normalize-path@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" - integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= - dependencies: - remove-trailing-separator "^1.0.1" - -normalize-path@^3.0.0, normalize-path@~3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" - integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== - -normalize-range@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" - integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI= - -normalize-url@^3.0.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559" - integrity sha512-U+JJi7duF1o+u2pynbp2zXDW2/PADgC30f0GsHZtRh+HOcXHnw137TrNlyxxRvWW5fjKd3bcLHPxofWuCjaeZg== - -normalize-url@^4.1.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz#453354087e6ca96957bd8f5baf753f5982142129" - integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ== - -npm-bundled@^1.0.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-1.1.1.tgz#1edd570865a94cdb1bc8220775e29466c9fb234b" - integrity sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA== - dependencies: - npm-normalize-package-bin "^1.0.1" - -npm-conf@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/npm-conf/-/npm-conf-1.1.3.tgz#256cc47bd0e218c259c4e9550bf413bc2192aff9" - integrity sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw== - dependencies: - config-chain "^1.1.11" - pify "^3.0.0" - -npm-install-checks@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/npm-install-checks/-/npm-install-checks-4.0.0.tgz#a37facc763a2fde0497ef2c6d0ac7c3fbe00d7b4" - integrity sha512-09OmyDkNLYwqKPOnbI8exiOZU2GVVmQp7tgez2BPi5OZC8M82elDAps7sxC4l//uSUtotWqoEIDwjRvWH4qz8w== - dependencies: - semver "^7.1.1" - -npm-normalize-package-bin@^1.0.0, npm-normalize-package-bin@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz#6e79a41f23fd235c0623218228da7d9c23b8f6e2" - integrity sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA== - -npm-package-arg@8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-8.1.0.tgz#b5f6319418c3246a1c38e1a8fbaa06231bc5308f" - integrity sha512-/ep6QDxBkm9HvOhOg0heitSd7JHA1U7y1qhhlRlteYYAi9Pdb/ZV7FW5aHpkrpM8+P+4p/jjR8zCyKPBMBjSig== - dependencies: - hosted-git-info "^3.0.6" - semver "^7.0.0" - validate-npm-package-name "^3.0.0" - -npm-package-arg@^6.0.0, npm-package-arg@^6.1.0: - version "6.1.1" - resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-6.1.1.tgz#02168cb0a49a2b75bf988a28698de7b529df5cb7" - integrity sha512-qBpssaL3IOZWi5vEKUKW0cO7kzLeT+EQO9W8RsLOZf76KF9E/K9+wH0C7t06HXPpaH8WH5xF1MExLuCwbTqRUg== - dependencies: - hosted-git-info "^2.7.1" - osenv "^0.1.5" - semver "^5.6.0" - validate-npm-package-name "^3.0.0" - -npm-package-arg@^8.0.0: - version "8.1.1" - resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-8.1.1.tgz#00ebf16ac395c63318e67ce66780a06db6df1b04" - integrity sha512-CsP95FhWQDwNqiYS+Q0mZ7FAEDytDZAkNxQqea6IaAFJTAY9Lhhqyl0irU/6PMc7BGfUmnsbHcqxJD7XuVM/rg== - dependencies: - hosted-git-info "^3.0.6" - semver "^7.0.0" - validate-npm-package-name "^3.0.0" - -npm-packlist@^1.1.12: - version "1.4.8" - resolved "https://registry.yarnpkg.com/npm-packlist/-/npm-packlist-1.4.8.tgz#56ee6cc135b9f98ad3d51c1c95da22bbb9b2ef3e" - integrity sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A== - dependencies: - ignore-walk "^3.0.1" - npm-bundled "^1.0.1" - npm-normalize-package-bin "^1.0.1" - -npm-pick-manifest@6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-6.1.0.tgz#2befed87b0fce956790f62d32afb56d7539c022a" - integrity sha512-ygs4k6f54ZxJXrzT0x34NybRlLeZ4+6nECAIbr2i0foTnijtS1TJiyzpqtuUAJOps/hO0tNDr8fRV5g+BtRlTw== - dependencies: - npm-install-checks "^4.0.0" - npm-package-arg "^8.0.0" - semver "^7.0.0" - -npm-pick-manifest@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/npm-pick-manifest/-/npm-pick-manifest-3.0.2.tgz#f4d9e5fd4be2153e5f4e5f9b7be8dc419a99abb7" - integrity sha512-wNprTNg+X5nf+tDi+hbjdHhM4bX+mKqv6XmPh7B5eG+QY9VARfQPfCEH013H5GqfNj6ee8Ij2fg8yk0mzps1Vw== - dependencies: - figgy-pudding "^3.5.1" - npm-package-arg "^6.0.0" - semver "^5.4.1" - -npm-registry-fetch@^4.0.0: - version "4.0.7" - resolved "https://registry.yarnpkg.com/npm-registry-fetch/-/npm-registry-fetch-4.0.7.tgz#57951bf6541e0246b34c9f9a38ab73607c9449d7" - integrity sha512-cny9v0+Mq6Tjz+e0erFAB+RYJ/AVGzkjnISiobqP8OWj9c9FLoZZu8/SPSKJWE17F1tk4018wfjV+ZbIbqC7fQ== - dependencies: - JSONStream "^1.3.4" - bluebird "^3.5.1" - figgy-pudding "^3.4.1" - lru-cache "^5.1.1" - make-fetch-happen "^5.0.0" - npm-package-arg "^6.1.0" - safe-buffer "^5.2.0" - -npm-run-path@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f" - integrity sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8= - dependencies: - path-key "^2.0.0" - -nth-check@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz#b2bd295c37e3dd58a3bf0700376663ba4d9cf05c" - integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg== - dependencies: - boolbase "~1.0.0" - -num2fraction@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede" - integrity sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4= - -oauth-sign@~0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" - integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ== - -object-assign@^4.0.1, object-assign@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= - -object-copy@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" - integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= - dependencies: - copy-descriptor "^0.1.0" - define-property "^0.2.5" - kind-of "^3.0.3" - -object-inspect@^1.9.0: - version "1.9.0" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.9.0.tgz#c90521d74e1127b67266ded3394ad6116986533a" - integrity sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw== - -object-is@^1.0.1: - version "1.1.5" - resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.5.tgz#b9deeaa5fc7f1846a0faecdceec138e5778f53ac" - integrity sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - -object-keys@^1.0.12, object-keys@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" - integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== - -object-visit@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" - integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= - dependencies: - isobject "^3.0.0" - -object.assign@^4.1.0, object.assign@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" - integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== - dependencies: - call-bind "^1.0.0" - define-properties "^1.1.3" - has-symbols "^1.0.1" - object-keys "^1.1.1" - -object.getownpropertydescriptors@^2.1.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.1.2.tgz#1bd63aeacf0d5d2d2f31b5e393b03a7c601a23f7" - integrity sha512-WtxeKSzfBjlzL+F9b7M7hewDzMwy+C8NRssHd1YrNlzHzIDrXcXiNOMrezdAEM4UXixgV+vvnyBeN7Rygl2ttQ== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.18.0-next.2" - -object.pick@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" - integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= - dependencies: - isobject "^3.0.1" - -object.values@^1.1.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.3.tgz#eaa8b1e17589f02f698db093f7c62ee1699742ee" - integrity sha512-nkF6PfDB9alkOUxpf1HNm/QlkeW3SReqL5WXeBLpEJJnlPSvRaDQpW3gQTksTN3fgJX4hL42RzKyOin6ff3tyw== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.18.0-next.2" - has "^1.0.3" - -obuf@^1.0.0, obuf@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" - integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== - -on-finished@~2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" - integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc= - dependencies: - ee-first "1.1.1" - -on-headers@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" - integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== - -once@^1.3.0, once@^1.3.1, once@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= - dependencies: - wrappy "1" - -onetime@^5.1.0: - version "5.1.2" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" - integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== - dependencies: - mimic-fn "^2.1.0" - -open@7.3.0: - version "7.3.0" - resolved "https://registry.yarnpkg.com/open/-/open-7.3.0.tgz#45461fdee46444f3645b6e14eb3ca94b82e1be69" - integrity sha512-mgLwQIx2F/ye9SmbrUkurZCnkoXyXyu9EbHtJZrICjVAJfyMArdHp3KkixGdZx1ZHFPNIwl0DDM1dFFqXbTLZw== - dependencies: - is-docker "^2.0.0" - is-wsl "^2.1.1" - -opn@^5.5.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/opn/-/opn-5.5.0.tgz#fc7164fab56d235904c51c3b27da6758ca3b9bfc" - integrity sha512-PqHpggC9bLV0VeWcdKhkpxY+3JTzetLSqTCWL/z/tFIbI6G8JCjondXklT1JinczLz2Xib62sSp0T/gKT4KksA== - dependencies: - is-wsl "^1.1.0" - -ora@5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/ora/-/ora-5.1.0.tgz#b188cf8cd2d4d9b13fd25383bc3e5cba352c94f8" - integrity sha512-9tXIMPvjZ7hPTbk8DFq1f7Kow/HU/pQYB60JbNq+QnGwcyhWVZaQ4hM9zQDEsPxw/muLpgiHSaumUZxCAmod/w== - dependencies: - chalk "^4.1.0" - cli-cursor "^3.1.0" - cli-spinners "^2.4.0" - is-interactive "^1.0.0" - log-symbols "^4.0.0" - mute-stream "0.0.8" - strip-ansi "^6.0.0" - wcwidth "^1.0.1" - -original@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/original/-/original-1.0.2.tgz#e442a61cffe1c5fd20a65f3261c26663b303f25f" - integrity sha512-hyBVl6iqqUOJ8FqRe+l/gS8H+kKYjrEndd5Pm1MfBtsEKA038HkkdbAl/72EAXGyonD/PFsvmVG+EvcIpliMBg== - dependencies: - url-parse "^1.4.3" - -os-browserify@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/os-browserify/-/os-browserify-0.3.0.tgz#854373c7f5c2315914fc9bfc6bd8238fdda1ec27" - integrity sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc= - -os-homedir@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" - integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= - -os-tmpdir@^1.0.0, os-tmpdir@~1.0.1, os-tmpdir@~1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" - integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= - -osenv@^0.1.5: - version "0.1.5" - resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.5.tgz#85cdfafaeb28e8677f416e287592b5f3f49ea410" - integrity sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g== - dependencies: - os-homedir "^1.0.0" - os-tmpdir "^1.0.0" - -p-cancelable@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz#d078d15a3af409220c886f1d9a0ca2e441ab26cc" - integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== - -p-finally@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" - integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4= - -p-limit@^2.0.0, p-limit@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" - integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== - dependencies: - p-try "^2.0.0" - -p-limit@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" - integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== - dependencies: - yocto-queue "^0.1.0" - -p-locate@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-3.0.0.tgz#322d69a05c0264b25997d9f40cd8a891ab0064a4" - integrity sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ== - dependencies: - p-limit "^2.0.0" - -p-locate@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" - integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== - dependencies: - p-limit "^2.2.0" - -p-map@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175" - integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw== - -p-map@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b" - integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ== - dependencies: - aggregate-error "^3.0.0" - -p-retry@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-3.0.1.tgz#316b4c8893e2c8dc1cfa891f406c4b422bebf328" - integrity sha512-XE6G4+YTTkT2a0UWb2kjZe8xNwf8bIbnqpc/IS/idOBVhyves0mK5OJgeocjx7q5pvX/6m23xuzVPYT1uGM73w== - dependencies: - retry "^0.12.0" - -p-try@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" - integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== - -pacote@9.5.12: - version "9.5.12" - resolved "https://registry.yarnpkg.com/pacote/-/pacote-9.5.12.tgz#1e11dd7a8d736bcc36b375a9804d41bb0377bf66" - integrity sha512-BUIj/4kKbwWg4RtnBncXPJd15piFSVNpTzY0rysSr3VnMowTYgkGKcaHrbReepAkjTr8lH2CVWRi58Spg2CicQ== - dependencies: - bluebird "^3.5.3" - cacache "^12.0.2" - chownr "^1.1.2" - figgy-pudding "^3.5.1" - get-stream "^4.1.0" - glob "^7.1.3" - infer-owner "^1.0.4" - lru-cache "^5.1.1" - make-fetch-happen "^5.0.0" - minimatch "^3.0.4" - minipass "^2.3.5" - mississippi "^3.0.0" - mkdirp "^0.5.1" - normalize-package-data "^2.4.0" - npm-normalize-package-bin "^1.0.0" - npm-package-arg "^6.1.0" - npm-packlist "^1.1.12" - npm-pick-manifest "^3.0.0" - npm-registry-fetch "^4.0.0" - osenv "^0.1.5" - promise-inflight "^1.0.1" - promise-retry "^1.1.1" - protoduck "^5.0.1" - rimraf "^2.6.2" - safe-buffer "^5.1.2" - semver "^5.6.0" - ssri "^6.0.1" - tar "^4.4.10" - unique-filename "^1.1.1" - which "^1.3.1" - -pako@~1.0.2, pako@~1.0.5: - version "1.0.11" - resolved "https://registry.yarnpkg.com/pako/-/pako-1.0.11.tgz#6c9599d340d54dfd3946380252a35705a6b992bf" - integrity sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw== - -parallel-transform@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/parallel-transform/-/parallel-transform-1.2.0.tgz#9049ca37d6cb2182c3b1d2c720be94d14a5814fc" - integrity sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg== - dependencies: - cyclist "^1.0.1" - inherits "^2.0.3" - readable-stream "^2.1.5" - -parent-module@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" - integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== - dependencies: - callsites "^3.0.0" - -parse-asn1@^5.0.0, parse-asn1@^5.1.5: - version "5.1.6" - resolved "https://registry.yarnpkg.com/parse-asn1/-/parse-asn1-5.1.6.tgz#385080a3ec13cb62a62d39409cb3e88844cdaed4" - integrity sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw== - dependencies: - asn1.js "^5.2.0" - browserify-aes "^1.0.0" - evp_bytestokey "^1.0.0" - pbkdf2 "^3.0.3" - safe-buffer "^5.1.1" - -parse-json@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-4.0.0.tgz#be35f5425be1f7f6c747184f98a788cb99477ee0" - integrity sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA= - dependencies: - error-ex "^1.3.1" - json-parse-better-errors "^1.0.1" - -parse-json@^5.0.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" - integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== - 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" - -parse5-html-rewriting-stream@6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/parse5-html-rewriting-stream/-/parse5-html-rewriting-stream-6.0.1.tgz#de1820559317ab4e451ea72dba05fddfd914480b" - integrity sha512-vwLQzynJVEfUlURxgnf51yAJDQTtVpNyGD8tKi2Za7m+akukNHxCcUQMAa/mUGLhCeicFdpy7Tlvj8ZNKadprg== - dependencies: - parse5 "^6.0.1" - parse5-sax-parser "^6.0.1" - -parse5-sax-parser@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/parse5-sax-parser/-/parse5-sax-parser-6.0.1.tgz#98b4d366b5b266a7cd90b4b58906667af882daba" - integrity sha512-kXX+5S81lgESA0LsDuGjAlBybImAChYRMT+/uKCEXFBFOeEhS52qUCydGhU3qLRD8D9DVjaUo821WK7DM4iCeg== - dependencies: - parse5 "^6.0.1" - -parse5@^5.0.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-5.1.1.tgz#f68e4e5ba1852ac2cadc00f4555fff6c2abb6178" - integrity sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug== - -parse5@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" - integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== - -parseqs@0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/parseqs/-/parseqs-0.0.6.tgz#8e4bb5a19d1cdc844a08ac974d34e273afa670d5" - integrity sha512-jeAGzMDbfSHHA091hr0r31eYfTig+29g3GKKE/PPbEQ65X0lmMwlEoqmhzu0iztID5uJpZsFlUPDP8ThPL7M8w== - -parseuri@0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/parseuri/-/parseuri-0.0.6.tgz#e1496e829e3ac2ff47f39a4dd044b32823c4a25a" - integrity sha512-AUjen8sAkGgao7UyCX6Ahv0gIK2fABKmYjvP4xmy5JaKvcbTRueIqIPHLAfq30xJddqSE033IOMUSOMCcK3Sow== - -parseurl@~1.3.2, parseurl@~1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" - integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== - -pascalcase@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" - integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= - -path-browserify@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-0.0.1.tgz#e6c4ddd7ed3aa27c68a20cc4e50e1a4ee83bbc4a" - integrity sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ== - -path-dirname@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" - integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA= - -path-exists@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" - integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= - -path-exists@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" - integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== - -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= - -path-is-inside@^1.0.1, path-is-inside@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" - integrity sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM= - -path-key@^2.0.0, path-key@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" - integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= - -path-parse@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" - integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== - -path-to-regexp@0.1.7: - version "0.1.7" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" - integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w= - -path-type@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" - integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== - -pbkdf2@^3.0.3: - version "3.1.1" - resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.1.tgz#cb8724b0fada984596856d1a6ebafd3584654b94" - integrity sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg== - dependencies: - create-hash "^1.1.2" - create-hmac "^1.1.4" - ripemd160 "^2.0.1" - safe-buffer "^5.0.1" - sha.js "^2.4.8" - -pend@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" - integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= - -performance-now@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" - integrity sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns= - -picomatch@^2.0.4, picomatch@^2.0.5, picomatch@^2.2.1: - version "2.2.2" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad" - integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg== - -pify@^2.0.0, pify@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" - integrity sha1-7RQaasBDqEnqWISY59yosVMw6Qw= - -pify@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz#e5a4acd2c101fdf3d9a4d07f0dbc4db49dd28176" - integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= - -pify@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" - integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== - -pinkie-promise@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" - integrity sha1-ITXW36ejWMBprJsXh3YogihFD/o= - dependencies: - pinkie "^2.0.0" - -pinkie@^2.0.0: - version "2.0.4" - resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" - integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= - -pkg-dir@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-3.0.0.tgz#2749020f239ed990881b1f71210d51eb6523bea3" - integrity sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw== - dependencies: - find-up "^3.0.0" - -pkg-dir@^4.1.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" - integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== - dependencies: - find-up "^4.0.0" - -pnp-webpack-plugin@1.6.4: - version "1.6.4" - resolved "https://registry.yarnpkg.com/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz#c9711ac4dc48a685dabafc86f8b6dd9f8df84149" - integrity sha512-7Wjy+9E3WwLOEL30D+m8TSTF7qJJUJLONBnwQp0518siuMxUQUbgZwssaFX+QKlZkjHZcw/IpZCt/H0srrntSg== - dependencies: - ts-pnp "^1.1.6" - -portfinder@^1.0.26: - version "1.0.28" - resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.28.tgz#67c4622852bd5374dd1dd900f779f53462fac778" - integrity sha512-Se+2isanIcEqf2XMHjyUKskczxbPH7dQnlMjXX6+dybayyHvAf/TCgyMRlzf/B6QDhAEFOGes0pzRo3by4AbMA== - dependencies: - async "^2.6.2" - debug "^3.1.1" - mkdirp "^0.5.5" - -posix-character-classes@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" - integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= - -postcss-calc@^7.0.1: - version "7.0.5" - resolved "https://registry.yarnpkg.com/postcss-calc/-/postcss-calc-7.0.5.tgz#f8a6e99f12e619c2ebc23cf6c486fdc15860933e" - integrity sha512-1tKHutbGtLtEZF6PT4JSihCHfIVldU72mZ8SdZHIYriIZ9fh9k9aWSppaT8rHsyI3dX+KSR+W+Ix9BMY3AODrg== - dependencies: - postcss "^7.0.27" - postcss-selector-parser "^6.0.2" - postcss-value-parser "^4.0.2" - -postcss-colormin@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/postcss-colormin/-/postcss-colormin-4.0.3.tgz#ae060bce93ed794ac71264f08132d550956bd381" - integrity sha512-WyQFAdDZpExQh32j0U0feWisZ0dmOtPl44qYmJKkq9xFWY3p+4qnRzCHeNrkeRhwPHz9bQ3mo0/yVkaply0MNw== - dependencies: - browserslist "^4.0.0" - color "^3.0.0" - has "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-convert-values@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-convert-values/-/postcss-convert-values-4.0.1.tgz#ca3813ed4da0f812f9d43703584e449ebe189a7f" - integrity sha512-Kisdo1y77KUC0Jmn0OXU/COOJbzM8cImvw1ZFsBgBgMgb1iL23Zs/LXRe3r+EZqM3vGYKdQ2YJVQ5VkJI+zEJQ== - dependencies: - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-discard-comments@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-discard-comments/-/postcss-discard-comments-4.0.2.tgz#1fbabd2c246bff6aaad7997b2b0918f4d7af4033" - integrity sha512-RJutN259iuRf3IW7GZyLM5Sw4GLTOH8FmsXBnv8Ab/Tc2k4SR4qbV4DNbyyY4+Sjo362SyDmW2DQ7lBSChrpkg== - dependencies: - postcss "^7.0.0" - -postcss-discard-duplicates@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-discard-duplicates/-/postcss-discard-duplicates-4.0.2.tgz#3fe133cd3c82282e550fc9b239176a9207b784eb" - integrity sha512-ZNQfR1gPNAiXZhgENFfEglF93pciw0WxMkJeVmw8eF+JZBbMD7jp6C67GqJAXVZP2BWbOztKfbsdmMp/k8c6oQ== - dependencies: - postcss "^7.0.0" - -postcss-discard-empty@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-discard-empty/-/postcss-discard-empty-4.0.1.tgz#c8c951e9f73ed9428019458444a02ad90bb9f765" - integrity sha512-B9miTzbznhDjTfjvipfHoqbWKwd0Mj+/fL5s1QOz06wufguil+Xheo4XpOnc4NqKYBCNqqEzgPv2aPBIJLox0w== - dependencies: - postcss "^7.0.0" - -postcss-discard-overridden@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-discard-overridden/-/postcss-discard-overridden-4.0.1.tgz#652aef8a96726f029f5e3e00146ee7a4e755ff57" - integrity sha512-IYY2bEDD7g1XM1IDEsUT4//iEYCxAmP5oDSFMVU/JVvT7gh+l4fmjciLqGgwjdWpQIdb0Che2VX00QObS5+cTg== - dependencies: - postcss "^7.0.0" - -postcss-import@12.0.1: - version "12.0.1" - resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-12.0.1.tgz#cf8c7ab0b5ccab5649024536e565f841928b7153" - integrity sha512-3Gti33dmCjyKBgimqGxL3vcV8w9+bsHwO5UrBawp796+jdardbcFl4RP5w/76BwNL7aGzpKstIfF9I+kdE8pTw== - dependencies: - postcss "^7.0.1" - postcss-value-parser "^3.2.3" - read-cache "^1.0.0" - resolve "^1.1.7" - -postcss-loader@4.0.4: - version "4.0.4" - resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-4.0.4.tgz#b2d005b52e008a44991cf8123bee207e635eb53e" - integrity sha512-pntA9zIR14drQo84yGTjQJg1m7T0DkXR4vXYHBngiRZdJtEeCrojL6lOpqUanMzG375lIJbT4Yug85zC/AJWGw== - dependencies: - cosmiconfig "^7.0.0" - klona "^2.0.4" - loader-utils "^2.0.0" - schema-utils "^3.0.0" - semver "^7.3.2" - -postcss-merge-longhand@^4.0.11: - version "4.0.11" - resolved "https://registry.yarnpkg.com/postcss-merge-longhand/-/postcss-merge-longhand-4.0.11.tgz#62f49a13e4a0ee04e7b98f42bb16062ca2549e24" - integrity sha512-alx/zmoeXvJjp7L4mxEMjh8lxVlDFX1gqWHzaaQewwMZiVhLo42TEClKaeHbRf6J7j82ZOdTJ808RtN0ZOZwvw== - dependencies: - css-color-names "0.0.4" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - stylehacks "^4.0.0" - -postcss-merge-rules@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/postcss-merge-rules/-/postcss-merge-rules-4.0.3.tgz#362bea4ff5a1f98e4075a713c6cb25aefef9a650" - integrity sha512-U7e3r1SbvYzO0Jr3UT/zKBVgYYyhAz0aitvGIYOYK5CPmkNih+WDSsS5tvPrJ8YMQYlEMvsZIiqmn7HdFUaeEQ== - dependencies: - browserslist "^4.0.0" - caniuse-api "^3.0.0" - cssnano-util-same-parent "^4.0.0" - postcss "^7.0.0" - postcss-selector-parser "^3.0.0" - vendors "^1.0.0" - -postcss-minify-font-values@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-minify-font-values/-/postcss-minify-font-values-4.0.2.tgz#cd4c344cce474343fac5d82206ab2cbcb8afd5a6" - integrity sha512-j85oO6OnRU9zPf04+PZv1LYIYOprWm6IA6zkXkrJXyRveDEuQggG6tvoy8ir8ZwjLxLuGfNkCZEQG7zan+Hbtg== - dependencies: - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-minify-gradients@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-minify-gradients/-/postcss-minify-gradients-4.0.2.tgz#93b29c2ff5099c535eecda56c4aa6e665a663471" - integrity sha512-qKPfwlONdcf/AndP1U8SJ/uzIJtowHlMaSioKzebAXSG4iJthlWC9iSWznQcX4f66gIWX44RSA841HTHj3wK+Q== - dependencies: - cssnano-util-get-arguments "^4.0.0" - is-color-stop "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-minify-params@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-minify-params/-/postcss-minify-params-4.0.2.tgz#6b9cef030c11e35261f95f618c90036d680db874" - integrity sha512-G7eWyzEx0xL4/wiBBJxJOz48zAKV2WG3iZOqVhPet/9geefm/Px5uo1fzlHu+DOjT+m0Mmiz3jkQzVHe6wxAWg== - dependencies: - alphanum-sort "^1.0.0" - browserslist "^4.0.0" - cssnano-util-get-arguments "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - uniqs "^2.0.0" - -postcss-minify-selectors@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-minify-selectors/-/postcss-minify-selectors-4.0.2.tgz#e2e5eb40bfee500d0cd9243500f5f8ea4262fbd8" - integrity sha512-D5S1iViljXBj9kflQo4YutWnJmwm8VvIsU1GeXJGiG9j8CIg9zs4voPMdQDUmIxetUOh60VilsNzCiAFTOqu3g== - dependencies: - alphanum-sort "^1.0.0" - has "^1.0.0" - postcss "^7.0.0" - postcss-selector-parser "^3.0.0" - -postcss-modules-extract-imports@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/postcss-modules-extract-imports/-/postcss-modules-extract-imports-2.0.0.tgz#818719a1ae1da325f9832446b01136eeb493cd7e" - integrity sha512-LaYLDNS4SG8Q5WAWqIJgdHPJrDDr/Lv775rMBFUbgjTz6j34lUznACHcdRWroPvXANP2Vj7yNK57vp9eFqzLWQ== - dependencies: - postcss "^7.0.5" - -postcss-modules-local-by-default@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/postcss-modules-local-by-default/-/postcss-modules-local-by-default-3.0.3.tgz#bb14e0cc78279d504dbdcbfd7e0ca28993ffbbb0" - integrity sha512-e3xDq+LotiGesympRlKNgaJ0PCzoUIdpH0dj47iWAui/kyTgh3CiAr1qP54uodmJhl6p9rN6BoNcdEDVJx9RDw== - dependencies: - icss-utils "^4.1.1" - postcss "^7.0.32" - postcss-selector-parser "^6.0.2" - postcss-value-parser "^4.1.0" - -postcss-modules-scope@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/postcss-modules-scope/-/postcss-modules-scope-2.2.0.tgz#385cae013cc7743f5a7d7602d1073a89eaae62ee" - integrity sha512-YyEgsTMRpNd+HmyC7H/mh3y+MeFWevy7V1evVhJWewmMbjDHIbZbOXICC2y+m1xI1UVfIT1HMW/O04Hxyu9oXQ== - dependencies: - postcss "^7.0.6" - postcss-selector-parser "^6.0.0" - -postcss-modules-values@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/postcss-modules-values/-/postcss-modules-values-3.0.0.tgz#5b5000d6ebae29b4255301b4a3a54574423e7f10" - integrity sha512-1//E5jCBrZ9DmRX+zCtmQtRSV6PV42Ix7Bzj9GbwJceduuf7IqP8MgeTXuRDHOWj2m0VzZD5+roFWDuU8RQjcg== - dependencies: - icss-utils "^4.0.0" - postcss "^7.0.6" - -postcss-normalize-charset@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-4.0.1.tgz#8b35add3aee83a136b0471e0d59be58a50285dd4" - integrity sha512-gMXCrrlWh6G27U0hF3vNvR3w8I1s2wOBILvA87iNXaPvSNo5uZAMYsZG7XjCUf1eVxuPfyL4TJ7++SGZLc9A3g== - dependencies: - postcss "^7.0.0" - -postcss-normalize-display-values@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-display-values/-/postcss-normalize-display-values-4.0.2.tgz#0dbe04a4ce9063d4667ed2be476bb830c825935a" - integrity sha512-3F2jcsaMW7+VtRMAqf/3m4cPFhPD3EFRgNs18u+k3lTJJlVe7d0YPO+bnwqo2xg8YiRpDXJI2u8A0wqJxMsQuQ== - dependencies: - cssnano-util-get-match "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-positions@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-positions/-/postcss-normalize-positions-4.0.2.tgz#05f757f84f260437378368a91f8932d4b102917f" - integrity sha512-Dlf3/9AxpxE+NF1fJxYDeggi5WwV35MXGFnnoccP/9qDtFrTArZ0D0R+iKcg5WsUd8nUYMIl8yXDCtcrT8JrdA== - dependencies: - cssnano-util-get-arguments "^4.0.0" - has "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-repeat-style@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-repeat-style/-/postcss-normalize-repeat-style-4.0.2.tgz#c4ebbc289f3991a028d44751cbdd11918b17910c" - integrity sha512-qvigdYYMpSuoFs3Is/f5nHdRLJN/ITA7huIoCyqqENJe9PvPmLhNLMu7QTjPdtnVf6OcYYO5SHonx4+fbJE1+Q== - dependencies: - cssnano-util-get-arguments "^4.0.0" - cssnano-util-get-match "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-string@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-string/-/postcss-normalize-string-4.0.2.tgz#cd44c40ab07a0c7a36dc5e99aace1eca4ec2690c" - integrity sha512-RrERod97Dnwqq49WNz8qo66ps0swYZDSb6rM57kN2J+aoyEAJfZ6bMx0sx/F9TIEX0xthPGCmeyiam/jXif0eA== - dependencies: - has "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-timing-functions@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-timing-functions/-/postcss-normalize-timing-functions-4.0.2.tgz#8e009ca2a3949cdaf8ad23e6b6ab99cb5e7d28d9" - integrity sha512-acwJY95edP762e++00Ehq9L4sZCEcOPyaHwoaFOhIwWCDfik6YvqsYNxckee65JHLKzuNSSmAdxwD2Cud1Z54A== - dependencies: - cssnano-util-get-match "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-unicode@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-normalize-unicode/-/postcss-normalize-unicode-4.0.1.tgz#841bd48fdcf3019ad4baa7493a3d363b52ae1cfb" - integrity sha512-od18Uq2wCYn+vZ/qCOeutvHjB5jm57ToxRaMeNuf0nWVHaP9Hua56QyMF6fs/4FSUnVIw0CBPsU0K4LnBPwYwg== - dependencies: - browserslist "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-url@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-normalize-url/-/postcss-normalize-url-4.0.1.tgz#10e437f86bc7c7e58f7b9652ed878daaa95faae1" - integrity sha512-p5oVaF4+IHwu7VpMan/SSpmpYxcJMtkGppYf0VbdH5B6hN8YNmVyJLuY9FmLQTzY3fag5ESUUHDqM+heid0UVA== - dependencies: - is-absolute-url "^2.0.0" - normalize-url "^3.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-normalize-whitespace@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-normalize-whitespace/-/postcss-normalize-whitespace-4.0.2.tgz#bf1d4070fe4fcea87d1348e825d8cc0c5faa7d82" - integrity sha512-tO8QIgrsI3p95r8fyqKV+ufKlSHh9hMJqACqbv2XknufqEDhDvbguXGBBqxw9nsQoXWf0qOqppziKJKHMD4GtA== - dependencies: - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-ordered-values@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/postcss-ordered-values/-/postcss-ordered-values-4.1.2.tgz#0cf75c820ec7d5c4d280189559e0b571ebac0eee" - integrity sha512-2fCObh5UanxvSxeXrtLtlwVThBvHn6MQcu4ksNT2tsaV2Fg76R2CV98W7wNSlX+5/pFwEyaDwKLLoEV7uRybAw== - dependencies: - cssnano-util-get-arguments "^4.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-reduce-initial@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/postcss-reduce-initial/-/postcss-reduce-initial-4.0.3.tgz#7fd42ebea5e9c814609639e2c2e84ae270ba48df" - integrity sha512-gKWmR5aUulSjbzOfD9AlJiHCGH6AEVLaM0AV+aSioxUDd16qXP1PCh8d1/BGVvpdWn8k/HiK7n6TjeoXN1F7DA== - dependencies: - browserslist "^4.0.0" - caniuse-api "^3.0.0" - has "^1.0.0" - postcss "^7.0.0" - -postcss-reduce-transforms@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-reduce-transforms/-/postcss-reduce-transforms-4.0.2.tgz#17efa405eacc6e07be3414a5ca2d1074681d4e29" - integrity sha512-EEVig1Q2QJ4ELpJXMZR8Vt5DQx8/mo+dGWSR7vWXqcob2gQLyQGsionYcGKATXvQzMPn6DSN1vTN7yFximdIAg== - dependencies: - cssnano-util-get-match "^4.0.0" - has "^1.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - -postcss-selector-parser@^3.0.0: - version "3.1.2" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-3.1.2.tgz#b310f5c4c0fdaf76f94902bbaa30db6aa84f5270" - integrity sha512-h7fJ/5uWuRVyOtkO45pnt1Ih40CEleeyCHzipqAZO2e5H20g25Y48uYnFUiShvY4rZWNJ/Bib/KVPmanaCtOhA== - dependencies: - dot-prop "^5.2.0" - indexes-of "^1.0.1" - uniq "^1.0.1" - -postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2: - version "6.0.4" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.4.tgz#56075a1380a04604c38b063ea7767a129af5c2b3" - integrity sha512-gjMeXBempyInaBqpp8gODmwZ52WaYsVOsfr4L4lDQ7n3ncD6mEyySiDtgzCT+NYC0mmeOLvtsF8iaEf0YT6dBw== - dependencies: - cssesc "^3.0.0" - indexes-of "^1.0.1" - uniq "^1.0.1" - util-deprecate "^1.0.2" - -postcss-svgo@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-4.0.2.tgz#17b997bc711b333bab143aaed3b8d3d6e3d38258" - integrity sha512-C6wyjo3VwFm0QgBy+Fu7gCYOkCmgmClghO+pjcxvrcBKtiKt0uCF+hvbMO1fyv5BMImRK90SMb+dwUnfbGd+jw== - dependencies: - is-svg "^3.0.0" - postcss "^7.0.0" - postcss-value-parser "^3.0.0" - svgo "^1.0.0" - -postcss-unique-selectors@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/postcss-unique-selectors/-/postcss-unique-selectors-4.0.1.tgz#9446911f3289bfd64c6d680f073c03b1f9ee4bac" - integrity sha512-+JanVaryLo9QwZjKrmJgkI4Fn8SBgRO6WXQBJi7KiAVPlmxikB5Jzc4EvXMT2H0/m0RjrVVm9rGNhZddm/8Spg== - dependencies: - alphanum-sort "^1.0.0" - postcss "^7.0.0" - uniqs "^2.0.0" - -postcss-value-parser@^3.0.0, postcss-value-parser@^3.2.3: - version "3.3.1" - resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-3.3.1.tgz#9ff822547e2893213cf1c30efa51ac5fd1ba8281" - integrity sha512-pISE66AbVkp4fDQ7VHBwRNXzAAKJjw4Vw7nWI/+Q3vuly7SNfgYXvm6i5IgFylHGK5sP/xHAbB7N49OS4gWNyQ== - -postcss-value-parser@^4.0.2, postcss-value-parser@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.1.0.tgz#443f6a20ced6481a2bda4fa8532a6e55d789a2cb" - integrity sha512-97DXOFbQJhk71ne5/Mt6cOu6yxsSfM0QGQyl0L25Gca4yGWEGJaig7l7gbCX623VqTBNGLRLaVUCnNkcedlRSQ== - -postcss@7.0.21: - version "7.0.21" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.21.tgz#06bb07824c19c2021c5d056d5b10c35b989f7e17" - integrity sha512-uIFtJElxJo29QC753JzhidoAhvp/e/Exezkdhfmt8AymWT6/5B7W1WmponYWkHk2eg6sONyTch0A3nkMPun3SQ== - dependencies: - chalk "^2.4.2" - source-map "^0.6.1" - supports-color "^6.1.0" - -postcss@7.0.32: - version "7.0.32" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.32.tgz#4310d6ee347053da3433db2be492883d62cec59d" - integrity sha512-03eXong5NLnNCD05xscnGKGDZ98CyzoqPSMjOe6SuoQY7Z2hIj0Ld1g/O/UQRuOle2aRtiIRDg9tDcTGAkLfKw== - dependencies: - chalk "^2.4.2" - source-map "^0.6.1" - supports-color "^6.1.0" - -postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.27, postcss@^7.0.32, postcss@^7.0.5, postcss@^7.0.6: - version "7.0.35" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.35.tgz#d2be00b998f7f211d8a276974079f2e92b970e24" - integrity sha512-3QT8bBJeX/S5zKTTjTCIjRF3If4avAT6kqxcASlTWEtAFCb9NH0OUxNDfgZSWdP5fJnBYCMEWkIFfWeugjzYMg== - dependencies: - chalk "^2.4.2" - source-map "^0.6.1" - supports-color "^6.1.0" - -prepend-http@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897" - integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= - -process-nextick-args@~2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" - integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== - -process@^0.11.10: - version "0.11.10" - resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" - integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI= - -progress@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" - integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== - -promise-inflight@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" - integrity sha1-mEcocL8igTL8vdhoEputEsPAKeM= - -promise-retry@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/promise-retry/-/promise-retry-1.1.1.tgz#6739e968e3051da20ce6497fb2b50f6911df3d6d" - integrity sha1-ZznpaOMFHaIM5kl/srUPaRHfPW0= - dependencies: - err-code "^1.0.0" - retry "^0.10.0" - -proto-list@~1.2.1: - version "1.2.4" - resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" - integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk= - -protoduck@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/protoduck/-/protoduck-5.0.1.tgz#03c3659ca18007b69a50fd82a7ebcc516261151f" - integrity sha512-WxoCeDCoCBY55BMvj4cAEjdVUFGRWed9ZxPlqTKYyw1nDDTQ4pqmnIMAGfJlg7Dx35uB/M+PHJPTmGOvaCaPTg== - dependencies: - genfun "^5.0.0" - -protractor@~7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/protractor/-/protractor-7.0.0.tgz#c3e263608bd72e2c2dc802b11a772711a4792d03" - integrity sha512-UqkFjivi4GcvUQYzqGYNe0mLzfn5jiLmO8w9nMhQoJRLhy2grJonpga2IWhI6yJO30LibWXJJtA4MOIZD2GgZw== - dependencies: - "@types/q" "^0.0.32" - "@types/selenium-webdriver" "^3.0.0" - blocking-proxy "^1.0.0" - browserstack "^1.5.1" - chalk "^1.1.3" - glob "^7.0.3" - jasmine "2.8.0" - jasminewd2 "^2.1.0" - q "1.4.1" - saucelabs "^1.5.0" - selenium-webdriver "3.6.0" - source-map-support "~0.4.0" - webdriver-js-extender "2.1.0" - webdriver-manager "^12.1.7" - yargs "^15.3.1" - -proxy-addr@~2.0.5: - version "2.0.6" - resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf" - integrity sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw== - dependencies: - forwarded "~0.1.2" - ipaddr.js "1.9.1" - -prr@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" - integrity sha1-0/wRS6BplaRexok/SEzrHXj19HY= - -psl@^1.1.28: - version "1.8.0" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" - integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ== - -public-encrypt@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/public-encrypt/-/public-encrypt-4.0.3.tgz#4fcc9d77a07e48ba7527e7cbe0de33d0701331e0" - integrity sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q== - dependencies: - bn.js "^4.1.0" - browserify-rsa "^4.0.0" - create-hash "^1.1.0" - parse-asn1 "^5.0.0" - randombytes "^2.0.1" - safe-buffer "^5.1.2" - -pump@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" - integrity sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - -pump@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" - integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - -pumpify@^1.3.3: - version "1.5.1" - resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce" - integrity sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ== - dependencies: - duplexify "^3.6.0" - inherits "^2.0.3" - pump "^2.0.0" - -punycode@1.3.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.3.2.tgz#9653a036fb7c1ee42342f2325cceefea3926c48d" - integrity sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0= - -punycode@^1.2.4: - version "1.4.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" - integrity sha1-wNWmOycYgArY4esPpSachN1BhF4= - -punycode@^2.1.0, punycode@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== - -q@1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/q/-/q-1.4.1.tgz#55705bcd93c5f3673530c2c2cbc0c2b3addc286e" - integrity sha1-VXBbzZPF82c1MMLCy8DCs63cKG4= - -q@^1.1.2, q@^1.4.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" - integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc= - -qjobs@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/qjobs/-/qjobs-1.2.0.tgz#c45e9c61800bd087ef88d7e256423bdd49e5d071" - integrity sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg== - -qs@6.7.0: - version "6.7.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.7.0.tgz#41dc1a015e3d581f1621776be31afb2876a9b1bc" - integrity sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ== - -qs@~6.5.2: - version "6.5.2" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" - integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA== - -querystring-es3@^0.2.0: - version "0.2.1" - resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73" - integrity sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM= - -querystring@0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/querystring/-/querystring-0.2.0.tgz#b209849203bb25df820da756e747005878521620" - integrity sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA= - -querystringify@^2.1.1: - version "2.2.0" - resolved "https://registry.yarnpkg.com/querystringify/-/querystringify-2.2.0.tgz#3345941b4153cb9d082d8eee4cda2016a9aef7f6" - integrity sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ== - -queue-microtask@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.2.tgz#abf64491e6ecf0f38a6502403d4cda04f372dfd3" - integrity sha512-dB15eXv3p2jDlbOiNLyMabYg1/sXvppd8DP2J3EOCQ0AkuSXCW2tP7mnVouVLJKgUMY6yP0kcQDVpLCN13h4Xg== - -randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" - integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== - dependencies: - safe-buffer "^5.1.0" - -randomfill@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/randomfill/-/randomfill-1.0.4.tgz#c92196fc86ab42be983f1bf31778224931d61458" - integrity sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw== - dependencies: - randombytes "^2.0.5" - safe-buffer "^5.1.0" - -range-parser@^1.2.1, range-parser@~1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" - integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== - -raw-body@2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.0.tgz#a1ce6fb9c9bc356ca52e89256ab59059e13d0332" - integrity sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q== - dependencies: - bytes "3.1.0" - http-errors "1.7.2" - iconv-lite "0.4.24" - unpipe "1.0.0" - -raw-loader@4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/raw-loader/-/raw-loader-4.0.2.tgz#1aac6b7d1ad1501e66efdac1522c73e59a584eb6" - integrity sha512-ZnScIV3ag9A4wPX/ZayxL/jZH+euYb6FcUinPcgiQW0+UBtEv0O6Q3lGd3cqJ+GHH+rksEv3Pj99oxJ3u3VIKA== - dependencies: - loader-utils "^2.0.0" - schema-utils "^3.0.0" - -read-cache@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/read-cache/-/read-cache-1.0.0.tgz#e664ef31161166c9751cdbe8dbcf86b5fb58f774" - integrity sha1-5mTvMRYRZsl1HNvo28+GtftY93Q= - dependencies: - pify "^2.3.0" - -"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6, readable-stream@~2.3.6: - version "2.3.7" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" - integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" - -readable-stream@^3.0.6, readable-stream@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" - integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== - dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" - -readdirp@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" - integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ== - dependencies: - graceful-fs "^4.1.11" - micromatch "^3.1.10" - readable-stream "^2.0.2" - -readdirp@~3.5.0: - version "3.5.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.5.0.tgz#9ba74c019b15d365278d2e91bb8c48d7b4d42c9e" - integrity sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ== - dependencies: - picomatch "^2.2.1" - -reflect-metadata@^0.1.2: - version "0.1.13" - resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" - integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg== - -regenerate-unicode-properties@^8.2.0: - version "8.2.0" - resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz#e5de7111d655e7ba60c057dbe9ff37c87e65cdec" - integrity sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA== - dependencies: - regenerate "^1.4.0" - -regenerate@^1.4.0: - version "1.4.2" - resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" - integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== - -regenerator-runtime@0.13.7, regenerator-runtime@^0.13.4: - version "0.13.7" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" - integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew== - -regenerator-transform@^0.14.2: - version "0.14.5" - resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.5.tgz#c98da154683671c9c4dcb16ece736517e1b7feb4" - integrity sha512-eOf6vka5IO151Jfsw2NO9WpGX58W6wWmefK3I1zEGr0lOD0u8rwPaNqQL1aRxUaxLeKO3ArNh3VYg1KbaD+FFw== - dependencies: - "@babel/runtime" "^7.8.4" - -regex-not@^1.0.0, regex-not@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" - integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== - dependencies: - extend-shallow "^3.0.2" - safe-regex "^1.1.0" - -regex-parser@^2.2.11: - version "2.2.11" - resolved "https://registry.yarnpkg.com/regex-parser/-/regex-parser-2.2.11.tgz#3b37ec9049e19479806e878cabe7c1ca83ccfe58" - integrity sha512-jbD/FT0+9MBU2XAZluI7w2OBs1RBi6p9M83nkoZayQXXU9e8Robt69FcZc7wU4eJD/YFTjn1JdCk3rbMJajz8Q== - -regexp.prototype.flags@^1.2.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz#7ef352ae8d159e758c0eadca6f8fcb4eef07be26" - integrity sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - -regexpu-core@^4.7.1: - version "4.7.1" - resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.7.1.tgz#2dea5a9a07233298fbf0db91fa9abc4c6e0f8ad6" - integrity sha512-ywH2VUraA44DZQuRKzARmw6S66mr48pQVva4LBeRhcOltJ6hExvWly5ZjFLYo67xbIxb6W1q4bAGtgfEl20zfQ== - dependencies: - regenerate "^1.4.0" - regenerate-unicode-properties "^8.2.0" - regjsgen "^0.5.1" - regjsparser "^0.6.4" - unicode-match-property-ecmascript "^1.0.4" - unicode-match-property-value-ecmascript "^1.2.0" - -regjsgen@^0.5.1: - version "0.5.2" - resolved "https://registry.yarnpkg.com/regjsgen/-/regjsgen-0.5.2.tgz#92ff295fb1deecbf6ecdab2543d207e91aa33733" - integrity sha512-OFFT3MfrH90xIW8OOSyUrk6QHD5E9JOTeGodiJeBS3J6IwlgzJMNE/1bZklWz5oTg+9dCMyEetclvCVXOPoN3A== - -regjsparser@^0.6.4: - version "0.6.7" - resolved "https://registry.yarnpkg.com/regjsparser/-/regjsparser-0.6.7.tgz#c00164e1e6713c2e3ee641f1701c4b7aa0a7f86c" - integrity sha512-ib77G0uxsA2ovgiYbCVGx4Pv3PSttAx2vIwidqQzbL2U5S4Q+j00HdSAneSBuyVcMvEnTXMjiGgB+DlXozVhpQ== - dependencies: - jsesc "~0.5.0" - -remove-trailing-separator@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" - integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= - -repeat-element@^1.1.2: - version "1.1.3" - resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.3.tgz#782e0d825c0c5a3bb39731f84efee6b742e6b1ce" - integrity sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g== - -repeat-string@^1.6.1: - version "1.6.1" - resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" - integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= - -request@^2.87.0, request@^2.88.2: - version "2.88.2" - resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" - integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== - 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" - -require-directory@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" - integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= - -require-main-filename@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" - integrity sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg== - -requires-port@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" - integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= - -resolve-cwd@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-2.0.0.tgz#00a9f7387556e27038eae232caa372a6a59b665a" - integrity sha1-AKn3OHVW4nA46uIyyqNypqWbZlo= - dependencies: - resolve-from "^3.0.0" - -resolve-from@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" - integrity sha1-six699nWiBvItuZTM17rywoYh0g= - -resolve-from@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" - integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== - -resolve-url-loader@3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/resolve-url-loader/-/resolve-url-loader-3.1.2.tgz#235e2c28e22e3e432ba7a5d4e305c59a58edfc08" - integrity sha512-QEb4A76c8Mi7I3xNKXlRKQSlLBwjUV/ULFMP+G7n3/7tJZ8MG5wsZ3ucxP1Jz8Vevn6fnJsxDx9cIls+utGzPQ== - dependencies: - adjust-sourcemap-loader "3.0.0" - camelcase "5.3.1" - compose-function "3.0.3" - convert-source-map "1.7.0" - es6-iterator "2.0.3" - loader-utils "1.2.3" - postcss "7.0.21" - rework "1.0.1" - rework-visit "1.0.0" - source-map "0.6.1" - -resolve-url@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" - integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= - -resolve@1.18.1: - version "1.18.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.18.1.tgz#018fcb2c5b207d2a6424aee361c5a266da8f4130" - integrity sha512-lDfCPaMKfOJXjy0dPayzPdF1phampNWr3qFCjAu+rw/qbQmr5jWH5xN2hwh9QKfw9E5v4hwV7A+jrCmL8yjjqA== - dependencies: - is-core-module "^2.0.0" - path-parse "^1.0.6" - -resolve@^1.1.7, resolve@^1.10.0, resolve@^1.3.2, resolve@^1.8.1: - version "1.20.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" - integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== - dependencies: - is-core-module "^2.2.0" - path-parse "^1.0.6" - -responselike@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7" - integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= - dependencies: - lowercase-keys "^1.0.0" - -restore-cursor@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" - integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== - dependencies: - onetime "^5.1.0" - signal-exit "^3.0.2" - -ret@~0.1.10: - version "0.1.15" - resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" - integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== - -retry@^0.10.0: - version "0.10.1" - resolved "https://registry.yarnpkg.com/retry/-/retry-0.10.1.tgz#e76388d217992c252750241d3d3956fed98d8ff4" - integrity sha1-52OI0heZLCUnUCQdPTlW/tmNj/Q= - -retry@^0.12.0: - version "0.12.0" - resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" - integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs= - -reusify@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" - integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== - -rework-visit@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/rework-visit/-/rework-visit-1.0.0.tgz#9945b2803f219e2f7aca00adb8bc9f640f842c9a" - integrity sha1-mUWygD8hni96ygCtuLyfZA+ELJo= - -rework@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/rework/-/rework-1.0.1.tgz#30806a841342b54510aa4110850cd48534144aa7" - integrity sha1-MIBqhBNCtUUQqkEQhQzUhTQUSqc= - dependencies: - convert-source-map "^0.3.3" - css "^2.0.0" - -rfdc@^1.1.4: - version "1.2.0" - resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.2.0.tgz#9e9894258f48f284b43c3143c68070a4f373b949" - integrity sha512-ijLyszTMmUrXvjSooucVQwimGUk84eRcmCuLV8Xghe3UO85mjUtRAHRyoMM6XtyqbECaXuBWx18La3523sXINA== - -rgb-regex@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/rgb-regex/-/rgb-regex-1.0.1.tgz#c0e0d6882df0e23be254a475e8edd41915feaeb1" - integrity sha1-wODWiC3w4jviVKR16O3UGRX+rrE= - -rgba-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3" - integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM= - -rimraf@3.0.2, rimraf@^3.0.0, rimraf@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" - integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== - dependencies: - glob "^7.1.3" - -rimraf@^2.2.8, rimraf@^2.5.2, rimraf@^2.5.4, rimraf@^2.6.2, rimraf@^2.6.3: - version "2.7.1" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec" - integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w== - dependencies: - glob "^7.1.3" - -ripemd160@^2.0.0, ripemd160@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/ripemd160/-/ripemd160-2.0.2.tgz#a1c1a6f624751577ba5d07914cbc92850585890c" - integrity sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA== - dependencies: - hash-base "^3.0.0" - inherits "^2.0.1" - -roarr@^2.15.3: - version "2.15.4" - resolved "https://registry.yarnpkg.com/roarr/-/roarr-2.15.4.tgz#f5fe795b7b838ccfe35dc608e0282b9eba2e7afd" - integrity sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A== - dependencies: - boolean "^3.0.1" - detect-node "^2.0.4" - globalthis "^1.0.1" - json-stringify-safe "^5.0.1" - semver-compare "^1.0.0" - sprintf-js "^1.1.2" - -rollup@2.32.1: - version "2.32.1" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.32.1.tgz#625a92c54f5b4d28ada12d618641491d4dbb548c" - integrity sha512-Op2vWTpvK7t6/Qnm1TTh7VjEZZkN8RWgf0DHbkKzQBwNf748YhXbozHVefqpPp/Fuyk/PQPAnYsBxAEtlMvpUw== - optionalDependencies: - fsevents "~2.1.2" - -run-async@^2.4.0: - version "2.4.1" - resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" - integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== - -run-parallel@^1.1.9: - version "1.2.0" - resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" - integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== - dependencies: - queue-microtask "^1.2.2" - -run-queue@^1.0.0, run-queue@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/run-queue/-/run-queue-1.0.3.tgz#e848396f057d223f24386924618e25694161ec47" - integrity sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec= - dependencies: - aproba "^1.1.1" - -rxjs@6.6.3: - version "6.6.3" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.3.tgz#8ca84635c4daa900c0d3967a6ee7ac60271ee552" - integrity sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ== - dependencies: - tslib "^1.9.0" - -rxjs@^6.5.3, rxjs@^6.6.0: - version "6.6.6" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.6.tgz#14d8417aa5a07c5e633995b525e1e3c0dec03b70" - integrity sha512-/oTwee4N4iWzAMAL9xdGKjkEHmIwupR3oXbQjCKywF1BeFohswF3vZdogbmEF6pZkOsXTzWkrZszrWpQTByYVg== - dependencies: - tslib "^1.9.0" - -safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -safe-buffer@>=5.1.0, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@^5.1.2, safe-buffer@^5.2.0, safe-buffer@~5.2.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - -safe-regex@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" - integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= - dependencies: - ret "~0.1.10" - -"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0", safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@^2.1.2, safer-buffer@~2.1.0: - version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== - -sass-loader@10.0.5: - version "10.0.5" - resolved "https://registry.yarnpkg.com/sass-loader/-/sass-loader-10.0.5.tgz#f53505b5ddbedf43797470ceb34066ded82bb769" - integrity sha512-2LqoNPtKkZq/XbXNQ4C64GFEleSEHKv6NPSI+bMC/l+jpEXGJhiRYkAQToO24MR7NU4JRY2RpLpJ/gjo2Uf13w== - dependencies: - klona "^2.0.4" - loader-utils "^2.0.0" - neo-async "^2.6.2" - schema-utils "^3.0.0" - semver "^7.3.2" - -sass@1.27.0: - version "1.27.0" - resolved "https://registry.yarnpkg.com/sass/-/sass-1.27.0.tgz#0657ff674206b95ec20dc638a93e179c78f6ada2" - integrity sha512-0gcrER56OkzotK/GGwgg4fPrKuiFlPNitO7eUJ18Bs+/NBlofJfMxmxqpqJxjae9vu0Wq8TZzrSyxZal00WDig== - dependencies: - chokidar ">=2.0.0 <4.0.0" - -saucelabs@^1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/saucelabs/-/saucelabs-1.5.0.tgz#9405a73c360d449b232839919a86c396d379fd9d" - integrity sha512-jlX3FGdWvYf4Q3LFfFWS1QvPg3IGCGWxIc8QBFdPTbpTJnt/v17FHXYVAn7C8sHf1yUXo2c7yIM0isDryfYtHQ== - dependencies: - https-proxy-agent "^2.2.1" - -sax@>=0.6.0, sax@~1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" - integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== - -schema-utils@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-1.0.0.tgz#0b79a93204d7b600d4b2850d1f66c2a34951c770" - integrity sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g== - dependencies: - ajv "^6.1.0" - ajv-errors "^1.0.0" - ajv-keywords "^3.1.0" - -schema-utils@^2.6.5, schema-utils@^2.7.0, schema-utils@^2.7.1: - version "2.7.1" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.7.1.tgz#1ca4f32d1b24c590c203b8e7a50bf0ea4cd394d7" - integrity sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg== - dependencies: - "@types/json-schema" "^7.0.5" - ajv "^6.12.4" - ajv-keywords "^3.5.2" - -schema-utils@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.0.0.tgz#67502f6aa2b66a2d4032b4279a2944978a0913ef" - integrity sha512-6D82/xSzO094ajanoOSbe4YvXWMfn2A//8Y1+MUqFAJul5Bs+yn36xbK9OtNDcRVSBJ9jjeoXftM6CfztsjOAA== - dependencies: - "@types/json-schema" "^7.0.6" - ajv "^6.12.5" - ajv-keywords "^3.5.2" - -select-hose@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" - integrity sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo= - -selenium-webdriver@3.6.0, selenium-webdriver@^3.0.1: - version "3.6.0" - resolved "https://registry.yarnpkg.com/selenium-webdriver/-/selenium-webdriver-3.6.0.tgz#2ba87a1662c020b8988c981ae62cb2a01298eafc" - integrity sha512-WH7Aldse+2P5bbFBO4Gle/nuQOdVwpHMTL6raL3uuBj/vPG07k6uzt3aiahu352ONBr5xXh0hDlM3LhtXPOC4Q== - dependencies: - jszip "^3.1.3" - rimraf "^2.5.4" - tmp "0.0.30" - xml2js "^0.4.17" - -selfsigned@^1.10.7: - version "1.10.8" - resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.8.tgz#0d17208b7d12c33f8eac85c41835f27fc3d81a30" - integrity sha512-2P4PtieJeEwVgTU9QEcwIRDQ/mXJLX8/+I3ur+Pg16nS8oNbrGxEso9NyYWy8NAmXiNl4dlAp5MwoNeCWzON4w== - dependencies: - node-forge "^0.10.0" - -semver-compare@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz#0dee216a1c941ab37e9efb1788f6afc5ff5537fc" - integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w= - -semver-dsl@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/semver-dsl/-/semver-dsl-1.0.1.tgz#d3678de5555e8a61f629eed025366ae5f27340a0" - integrity sha1-02eN5VVeimH2Ke7QJTZq5fJzQKA= - dependencies: - semver "^5.3.0" - -semver-intersect@1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/semver-intersect/-/semver-intersect-1.4.0.tgz#bdd9c06bedcdd2fedb8cd352c3c43ee8c61321f3" - integrity sha512-d8fvGg5ycKAq0+I6nfWeCx6ffaWJCsBYU0H2Rq56+/zFePYfT8mXkB3tWBSjR5BerkHNZ5eTPIk1/LBYas35xQ== - dependencies: - semver "^5.0.0" - -"semver@2 || 3 || 4 || 5", semver@^5.0.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0: - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== - -semver@7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e" - integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A== - -semver@7.3.2: - version "7.3.2" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.2.tgz#604962b052b81ed0786aae84389ffba70ffd3938" - integrity sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ== - -semver@^6.0.0, semver@^6.2.0, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== - -semver@^7.0.0, semver@^7.1.1, semver@^7.3.2: - version "7.3.4" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz#27aaa7d2e4ca76452f98d3add093a72c943edc97" - integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw== - dependencies: - lru-cache "^6.0.0" - -send@0.17.1: - version "0.17.1" - resolved "https://registry.yarnpkg.com/send/-/send-0.17.1.tgz#c1d8b059f7900f7466dd4938bdc44e11ddb376c8" - integrity sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg== - 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.7.2" - mime "1.6.0" - ms "2.1.1" - on-finished "~2.3.0" - range-parser "~1.2.1" - statuses "~1.5.0" - -serialize-error@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-7.0.1.tgz#f1360b0447f61ffb483ec4157c737fab7d778e18" - integrity sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw== - dependencies: - type-fest "^0.13.1" - -serialize-javascript@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-4.0.0.tgz#b525e1238489a5ecfc42afacc3fe99e666f4b1aa" - integrity sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw== - dependencies: - randombytes "^2.1.0" - -serialize-javascript@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-5.0.1.tgz#7886ec848049a462467a97d3d918ebb2aaf934f4" - integrity sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA== - dependencies: - randombytes "^2.1.0" - -serve-index@^1.9.1: - version "1.9.1" - resolved "https://registry.yarnpkg.com/serve-index/-/serve-index-1.9.1.tgz#d3768d69b1e7d82e5ce050fff5b453bea12a9239" - integrity sha1-03aNabHn2C5c4FD/9bRTvqEqkjk= - 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" - -serve-static@1.14.1: - version "1.14.1" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.1.tgz#666e636dc4f010f7ef29970a88a674320898b2f9" - integrity sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg== - dependencies: - encodeurl "~1.0.2" - escape-html "~1.0.3" - parseurl "~1.3.3" - send "0.17.1" - -set-blocking@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" - integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc= - -set-immediate-shim@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" - integrity sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E= - -set-value@^2.0.0, set-value@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" - integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== - dependencies: - extend-shallow "^2.0.1" - is-extendable "^0.1.1" - is-plain-object "^2.0.3" - split-string "^3.0.1" - -setimmediate@^1.0.4: - version "1.0.5" - resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285" - integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU= - -setprototypeof@1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.0.tgz#d0bd85536887b6fe7c0d818cb962d9d91c54e656" - integrity sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ== - -setprototypeof@1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.1.1.tgz#7e95acb24aa92f5885e0abef5ba131330d4ae683" - integrity sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw== - -sha.js@^2.4.0, sha.js@^2.4.8: - version "2.4.11" - resolved "https://registry.yarnpkg.com/sha.js/-/sha.js-2.4.11.tgz#37a5cf0b81ecbc6943de109ba2960d1b26584ae7" - integrity sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ== - dependencies: - inherits "^2.0.1" - safe-buffer "^5.0.1" - -shallow-clone@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" - integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== - dependencies: - kind-of "^6.0.2" - -shebang-command@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea" - integrity sha1-RKrGW2lbAzmJaMOfNj/uXer98eo= - dependencies: - shebang-regex "^1.0.0" - -shebang-regex@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3" - integrity sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM= - -signal-exit@^3.0.0, signal-exit@^3.0.2: - version "3.0.3" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c" - integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== - -simple-swizzle@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" - integrity sha1-pNprY1/8zMoz9w0Xy5JZLeleVXo= - dependencies: - is-arrayish "^0.3.1" - -slash@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" - integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== - -smart-buffer@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.1.0.tgz#91605c25d91652f4661ea69ccf45f1b331ca21ba" - integrity sha512-iVICrxOzCynf/SNaBQCw34eM9jROU/s5rzIhpOvzhzuYHfJR/DhZfDkXiZSgKXfgv26HT3Yni3AV/DGw0cGnnw== - -snapdragon-node@^2.0.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" - integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== - dependencies: - define-property "^1.0.0" - isobject "^3.0.0" - snapdragon-util "^3.0.1" - -snapdragon-util@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" - integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== - dependencies: - kind-of "^3.2.0" - -snapdragon@^0.8.1: - version "0.8.2" - resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" - integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== - dependencies: - base "^0.11.1" - debug "^2.2.0" - define-property "^0.2.5" - extend-shallow "^2.0.1" - map-cache "^0.2.2" - source-map "^0.5.6" - source-map-resolve "^0.5.0" - use "^3.1.0" - -socket.io-adapter@~1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-1.1.2.tgz#ab3f0d6f66b8fc7fca3959ab5991f82221789be9" - integrity sha512-WzZRUj1kUjrTIrUKpZLEzFZ1OLj5FwLlAFQs9kuZJzJi5DKdU7FsWc36SNmA8iDOtwBQyT8FkrriRM8vXLYz8g== - -socket.io-client@2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/socket.io-client/-/socket.io-client-2.4.0.tgz#aafb5d594a3c55a34355562fc8aea22ed9119a35" - integrity sha512-M6xhnKQHuuZd4Ba9vltCLT9oa+YvTsP8j9NcEiLElfIg8KeYPyhWOes6x4t+LTAC8enQbE/995AdTem2uNyKKQ== - dependencies: - backo2 "1.0.2" - component-bind "1.0.0" - component-emitter "~1.3.0" - debug "~3.1.0" - engine.io-client "~3.5.0" - has-binary2 "~1.0.2" - indexof "0.0.1" - parseqs "0.0.6" - parseuri "0.0.6" - socket.io-parser "~3.3.0" - to-array "0.1.4" - -socket.io-parser@~3.3.0: - version "3.3.2" - resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.3.2.tgz#ef872009d0adcf704f2fbe830191a14752ad50b6" - integrity sha512-FJvDBuOALxdCI9qwRrO/Rfp9yfndRtc1jSgVgV8FDraihmSP/MLGD5PEuJrNfjALvcQ+vMDM/33AWOYP/JSjDg== - dependencies: - component-emitter "~1.3.0" - debug "~3.1.0" - isarray "2.0.1" - -socket.io-parser@~3.4.0: - version "3.4.1" - resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-3.4.1.tgz#b06af838302975837eab2dc980037da24054d64a" - integrity sha512-11hMgzL+WCLWf1uFtHSNvliI++tcRUWdoeYuwIl+Axvwy9z2gQM+7nJyN3STj1tLj5JyIUH8/gpDGxzAlDdi0A== - dependencies: - component-emitter "1.2.1" - debug "~4.1.0" - isarray "2.0.1" - -socket.io@^2.3.0: - version "2.4.1" - resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-2.4.1.tgz#95ad861c9a52369d7f1a68acf0d4a1b16da451d2" - integrity sha512-Si18v0mMXGAqLqCVpTxBa8MGqriHGQh8ccEOhmsmNS3thNCGBwO8WGrwMibANsWtQQ5NStdZwHqZR3naJVFc3w== - dependencies: - debug "~4.1.0" - engine.io "~3.5.0" - has-binary2 "~1.0.2" - socket.io-adapter "~1.1.0" - socket.io-client "2.4.0" - socket.io-parser "~3.4.0" - -sockjs-client@1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.4.0.tgz#c9f2568e19c8fd8173b4997ea3420e0bb306c7d5" - integrity sha512-5zaLyO8/nri5cua0VtOrFXBPK1jbL4+1cebT/mmKA1E1ZXOvJrII75bPu0l0k843G/+iAbhEqzyKr0w/eCCj7g== - dependencies: - debug "^3.2.5" - eventsource "^1.0.7" - faye-websocket "~0.11.1" - inherits "^2.0.3" - json3 "^3.3.2" - url-parse "^1.4.3" - -sockjs@0.3.20: - version "0.3.20" - resolved "https://registry.yarnpkg.com/sockjs/-/sockjs-0.3.20.tgz#b26a283ec562ef8b2687b44033a4eeceac75d855" - integrity sha512-SpmVOVpdq0DJc0qArhF3E5xsxvaiqGNb73XfgBpK1y3UD5gs8DSo8aCTsuT5pX8rssdc2NDIzANwP9eCAiSdTA== - dependencies: - faye-websocket "^0.10.0" - uuid "^3.4.0" - websocket-driver "0.6.5" - -socks-proxy-agent@^4.0.0: - version "4.0.2" - resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-4.0.2.tgz#3c8991f3145b2799e70e11bd5fbc8b1963116386" - integrity sha512-NT6syHhI9LmuEMSK6Kd2V7gNv5KFZoLE7V5udWmn0de+3Mkj3UMA/AJPLyeNUVmElCurSHtUdM3ETpR3z770Wg== - dependencies: - agent-base "~4.2.1" - socks "~2.3.2" - -socks@~2.3.2: - version "2.3.3" - resolved "https://registry.yarnpkg.com/socks/-/socks-2.3.3.tgz#01129f0a5d534d2b897712ed8aceab7ee65d78e3" - integrity sha512-o5t52PCNtVdiOvzMry7wU4aOqYWL0PeCXRWBEiJow4/i/wr+wpsJQ9awEu1EonLIqsfGd5qSgDdxEOvCdmBEpA== - dependencies: - ip "1.1.5" - smart-buffer "^4.1.0" - -source-list-map@^2.0.0, source-list-map@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-2.0.1.tgz#3993bd873bfc48479cca9ea3a547835c7c154b34" - integrity sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw== - -source-map-loader@1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-1.1.2.tgz#5b782bf08496d3a7f355e1780df0e25190a80991" - integrity sha512-bjf6eSENOYBX4JZDfl9vVLNsGAQ6Uz90fLmOazcmMcyDYOBFsGxPNn83jXezWLY9bJsVAo1ObztxPcV8HAbjVA== - dependencies: - abab "^2.0.5" - iconv-lite "^0.6.2" - loader-utils "^2.0.0" - schema-utils "^3.0.0" - source-map "^0.6.1" - whatwg-mimetype "^2.3.0" - -source-map-resolve@^0.5.0, source-map-resolve@^0.5.2: - version "0.5.3" - resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.3.tgz#190866bece7553e1f8f267a2ee82c606b5509a1a" - integrity sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw== - dependencies: - atob "^2.1.2" - decode-uri-component "^0.2.0" - resolve-url "^0.2.1" - source-map-url "^0.4.0" - urix "^0.1.0" - -source-map-support@0.5.19, source-map-support@^0.5.17, source-map-support@^0.5.5, source-map-support@~0.5.12, source-map-support@~0.5.19: - version "0.5.19" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz#a98b62f86dcaf4f67399648c085291ab9e8fed61" - integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - -source-map-support@~0.4.0: - version "0.4.18" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.4.18.tgz#0286a6de8be42641338594e97ccea75f0a2c585f" - integrity sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA== - dependencies: - source-map "^0.5.6" - -source-map-url@^0.4.0: - version "0.4.1" - resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.1.tgz#0af66605a745a5a2f91cf1bbf8a7afbc283dec56" - integrity sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw== - -source-map@0.6.1, source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - -source-map@0.7.3, source-map@^0.7.3, source-map@~0.7.2: - version "0.7.3" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383" - integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ== - -source-map@^0.5.0, source-map@^0.5.6, source-map@^0.5.7: - version "0.5.7" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" - integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= - -sourcemap-codec@^1.4.4, sourcemap-codec@^1.4.8: - version "1.4.8" - resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" - integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== - -spdx-correct@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" - integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== - dependencies: - spdx-expression-parse "^3.0.0" - spdx-license-ids "^3.0.0" - -spdx-exceptions@^2.1.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" - integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== - -spdx-expression-parse@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" - integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== - dependencies: - spdx-exceptions "^2.1.0" - spdx-license-ids "^3.0.0" - -spdx-license-ids@^3.0.0: - version "3.0.7" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz#e9c18a410e5ed7e12442a549fbd8afa767038d65" - integrity sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ== - -spdy-transport@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/spdy-transport/-/spdy-transport-3.0.0.tgz#00d4863a6400ad75df93361a1608605e5dcdcf31" - integrity sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw== - 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" - -spdy@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/spdy/-/spdy-4.0.2.tgz#b74f466203a3eda452c02492b91fb9e84a27677b" - integrity sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA== - 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" - -speed-measure-webpack-plugin@1.3.3: - version "1.3.3" - resolved "https://registry.yarnpkg.com/speed-measure-webpack-plugin/-/speed-measure-webpack-plugin-1.3.3.tgz#6ff894fc83e8a6310dde3af863a0329cd79da4f5" - integrity sha512-2ljD4Ch/rz2zG3HsLsnPfp23osuPBS0qPuz9sGpkNXTN1Ic4M+W9xB8l8rS8ob2cO4b1L+WTJw/0AJwWYVgcxQ== - dependencies: - chalk "^2.0.1" - -split-string@^3.0.1, split-string@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" - integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== - dependencies: - extend-shallow "^3.0.0" - -sprintf-js@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.2.tgz#da1765262bf8c0f571749f2ad6c26300207ae673" - integrity sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug== - -sprintf-js@~1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= - -sshpk@^1.7.0: - version "1.16.1" - resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" - integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== - 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" - -ssri@^6.0.0, ssri@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/ssri/-/ssri-6.0.1.tgz#2a3c41b28dd45b62b63676ecb74001265ae9edd8" - integrity sha512-3Wge10hNcT1Kur4PDFwEieXSCMCJs/7WvSACcrMYrNp+b8kDL1/0wJch5Ni2WrtwEa2IO8OsVfeKIciKCDx/QA== - dependencies: - figgy-pudding "^3.5.1" - -ssri@^8.0.0: - version "8.0.1" - resolved "https://registry.yarnpkg.com/ssri/-/ssri-8.0.1.tgz#638e4e439e2ffbd2cd289776d5ca457c4f51a2af" - integrity sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ== - dependencies: - minipass "^3.1.1" - -stable@^0.1.8: - version "0.1.8" - resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" - integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== - -static-extend@^0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" - integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= - dependencies: - define-property "^0.2.5" - object-copy "^0.1.0" - -"statuses@>= 1.4.0 < 2", "statuses@>= 1.5.0 < 2", statuses@~1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" - integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= - -stream-browserify@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/stream-browserify/-/stream-browserify-2.0.2.tgz#87521d38a44aa7ee91ce1cd2a47df0cb49dd660b" - integrity sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg== - dependencies: - inherits "~2.0.1" - readable-stream "^2.0.2" - -stream-each@^1.1.0: - version "1.2.3" - resolved "https://registry.yarnpkg.com/stream-each/-/stream-each-1.2.3.tgz#ebe27a0c389b04fbcc233642952e10731afa9bae" - integrity sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw== - dependencies: - end-of-stream "^1.1.0" - stream-shift "^1.0.0" - -stream-http@^2.7.2: - version "2.8.3" - resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc" - integrity sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw== - dependencies: - builtin-status-codes "^3.0.0" - inherits "^2.0.1" - readable-stream "^2.3.6" - to-arraybuffer "^1.0.0" - xtend "^4.0.0" - -stream-shift@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d" - integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ== - -streamroller@^2.2.4: - version "2.2.4" - resolved "https://registry.yarnpkg.com/streamroller/-/streamroller-2.2.4.tgz#c198ced42db94086a6193608187ce80a5f2b0e53" - integrity sha512-OG79qm3AujAM9ImoqgWEY1xG4HX+Lw+yY6qZj9R1K2mhF5bEmQ849wvrb+4vt4jLMLzwXttJlQbOdPOQVRv7DQ== - dependencies: - date-format "^2.1.0" - debug "^4.1.1" - fs-extra "^8.1.0" - -string-width@^3.0.0, string-width@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961" - integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== - dependencies: - emoji-regex "^7.0.1" - is-fullwidth-code-point "^2.0.0" - strip-ansi "^5.1.0" - -string-width@^4.1.0, string-width@^4.2.0: - version "4.2.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz#dafd4f9559a7585cfba529c6a0a4f73488ebd4c5" - integrity sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.0" - -string.prototype.trimend@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" - integrity sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - -string.prototype.trimstart@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" - integrity sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - -string_decoder@^1.0.0, string_decoder@^1.1.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== - dependencies: - safe-buffer "~5.2.0" - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - -strip-ansi@^3.0.0, strip-ansi@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" - integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= - dependencies: - ansi-regex "^2.0.0" - -strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae" - integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== - dependencies: - ansi-regex "^4.1.0" - -strip-ansi@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" - integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== - dependencies: - ansi-regex "^5.0.0" - -strip-eof@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf" - integrity sha1-u0P/VZim6wXYm1n80SnJgzE2Br8= - -style-loader@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-2.0.0.tgz#9669602fd4690740eaaec137799a03addbbc393c" - integrity sha512-Z0gYUJmzZ6ZdRUqpg1r8GsaFKypE+3xAzuFeMuoHgjc9KZv3wMyCRjQIWEbhoFSq7+7yoHXySDJyyWQaPajeiQ== - dependencies: - loader-utils "^2.0.0" - schema-utils "^3.0.0" - -stylehacks@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/stylehacks/-/stylehacks-4.0.3.tgz#6718fcaf4d1e07d8a1318690881e8d96726a71d5" - integrity sha512-7GlLk9JwlElY4Y6a/rmbH2MhVlTyVmiJd1PfTCqFaIBEGMYNsrO/v3SeGTdhBThLg4Z+NbOk/qFMwCa+J+3p/g== - dependencies: - browserslist "^4.0.0" - postcss "^7.0.0" - postcss-selector-parser "^3.0.0" - -stylus-loader@4.3.1: - version "4.3.1" - resolved "https://registry.yarnpkg.com/stylus-loader/-/stylus-loader-4.3.1.tgz#8b4e749294d9fe0729c2e5e1f04cbf87e1c941aa" - integrity sha512-apDYJEM5ZpOAWbWInWcsbtI8gHNr/XYVcSY/tWqOUPt7M5tqhtwXVsAkgyiVjhuvw2Yrjq474a9H+g4d047Ebw== - dependencies: - fast-glob "^3.2.4" - klona "^2.0.4" - loader-utils "^2.0.0" - normalize-path "^3.0.0" - schema-utils "^3.0.0" - -stylus@0.54.8: - version "0.54.8" - resolved "https://registry.yarnpkg.com/stylus/-/stylus-0.54.8.tgz#3da3e65966bc567a7b044bfe0eece653e099d147" - integrity sha512-vr54Or4BZ7pJafo2mpf0ZcwA74rpuYCZbxrHBsH8kbcXOwSfvBFwsRfpGO5OD5fhG5HDCFW737PKaawI7OqEAg== - dependencies: - css-parse "~2.0.0" - debug "~3.1.0" - glob "^7.1.6" - mkdirp "~1.0.4" - safer-buffer "^2.1.2" - sax "~1.2.4" - semver "^6.3.0" - source-map "^0.7.3" - -sumchecker@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/sumchecker/-/sumchecker-3.0.1.tgz#6377e996795abb0b6d348e9b3e1dfb24345a8e42" - integrity sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg== - dependencies: - debug "^4.1.0" - -supports-color@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" - integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= - -supports-color@^5.3.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" - integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== - dependencies: - has-flag "^3.0.0" - -supports-color@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-6.1.0.tgz#0764abc69c63d5ac842dd4867e8d025e880df8f3" - integrity sha512-qe1jfm1Mg7Nq/NSh6XE24gPXROEVsWHxC1LIx//XNlD9iw7YZQGjZNjYN7xGaEG6iKdA8EtNFW6R0gjnVXp+wQ== - dependencies: - has-flag "^3.0.0" - -supports-color@^7.0.0, supports-color@^7.1.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" - integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== - dependencies: - has-flag "^4.0.0" - -svgo@^1.0.0: - version "1.3.2" - resolved "https://registry.yarnpkg.com/svgo/-/svgo-1.3.2.tgz#b6dc511c063346c9e415b81e43401145b96d4167" - integrity sha512-yhy/sQYxR5BkC98CY7o31VGsg014AKLEPxdfhora76l36hD9Rdy5NZA/Ocn6yayNPgSamYdtX2rFJdcv07AYVw== - dependencies: - chalk "^2.4.1" - coa "^2.0.2" - css-select "^2.0.0" - css-select-base-adapter "^0.1.1" - css-tree "1.0.0-alpha.37" - csso "^4.0.2" - js-yaml "^3.13.1" - mkdirp "~0.5.1" - object.values "^1.1.0" - sax "~1.2.4" - stable "^0.1.8" - unquote "~1.1.1" - util.promisify "~1.0.0" - -symbol-observable@2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-2.0.3.tgz#5b521d3d07a43c351055fa43b8355b62d33fd16a" - integrity sha512-sQV7phh2WCYAn81oAkakC5qjq2Ml0g8ozqz03wOGnx9dDlG1de6yrF+0RAzSJD8fPUow3PTSMf2SAbOGxb93BA== - -tapable@^1.0.0, tapable@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/tapable/-/tapable-1.1.3.tgz#a1fccc06b58db61fd7a45da2da44f5f3a3e67ba2" - integrity sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== - -tapable@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.0.tgz#5c373d281d9c672848213d0e037d1c4165ab426b" - integrity sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw== - -tar@^4.4.10: - version "4.4.13" - resolved "https://registry.yarnpkg.com/tar/-/tar-4.4.13.tgz#43b364bc52888d555298637b10d60790254ab525" - integrity sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA== - dependencies: - chownr "^1.1.1" - fs-minipass "^1.2.5" - minipass "^2.8.6" - minizlib "^1.2.1" - mkdirp "^0.5.0" - safe-buffer "^5.1.2" - yallist "^3.0.3" - -tar@^6.0.2: - version "6.1.0" - resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.0.tgz#d1724e9bcc04b977b18d5c573b333a2207229a83" - integrity sha512-DUCttfhsnLCjwoDoFcI+B2iJgYa93vBnDUATYEeRx6sntCTdN01VnqsIuTlALXla/LWooNg0yEGeB+Y8WdFxGA== - 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" - -terser-webpack-plugin@4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-4.2.3.tgz#28daef4a83bd17c1db0297070adc07fc8cfc6a9a" - integrity sha512-jTgXh40RnvOrLQNgIkwEKnQ8rmHjHK4u+6UBEi+W+FPmvb+uo+chJXntKe7/3lW5mNysgSWD60KyesnhW8D6MQ== - dependencies: - cacache "^15.0.5" - find-cache-dir "^3.3.1" - jest-worker "^26.5.0" - p-limit "^3.0.2" - schema-utils "^3.0.0" - serialize-javascript "^5.0.1" - source-map "^0.6.1" - terser "^5.3.4" - webpack-sources "^1.4.3" - -terser-webpack-plugin@^1.4.3: - version "1.4.5" - resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-1.4.5.tgz#a217aefaea330e734ffacb6120ec1fa312d6040b" - integrity sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw== - dependencies: - cacache "^12.0.2" - find-cache-dir "^2.1.0" - is-wsl "^1.1.0" - schema-utils "^1.0.0" - serialize-javascript "^4.0.0" - source-map "^0.6.1" - terser "^4.1.2" - webpack-sources "^1.4.0" - worker-farm "^1.7.0" - -terser@5.3.7: - version "5.3.7" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.3.7.tgz#798a4ae2e7ff67050c3e99fcc4e00725827d97e2" - integrity sha512-lJbKdfxWvjpV330U4PBZStCT9h3N9A4zZVA5Y4k9sCWXknrpdyxi1oMsRKLmQ/YDMDxSBKIh88v0SkdhdqX06w== - dependencies: - commander "^2.20.0" - source-map "~0.7.2" - source-map-support "~0.5.19" - -terser@^4.1.2: - version "4.8.0" - resolved "https://registry.yarnpkg.com/terser/-/terser-4.8.0.tgz#63056343d7c70bb29f3af665865a46fe03a0df17" - integrity sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw== - dependencies: - commander "^2.20.0" - source-map "~0.6.1" - source-map-support "~0.5.12" - -terser@^5.3.4: - version "5.6.0" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.6.0.tgz#138cdf21c5e3100b1b3ddfddf720962f88badcd2" - integrity sha512-vyqLMoqadC1uR0vywqOZzriDYzgEkNJFK4q9GeyOBHIbiECHiWLKcWfbQWAUaPfxkjDhapSlZB9f7fkMrvkVjA== - dependencies: - commander "^2.20.0" - source-map "~0.7.2" - source-map-support "~0.5.19" - -text-table@0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" - integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= - -through2@^2.0.0: - version "2.0.5" - resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" - integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== - dependencies: - readable-stream "~2.3.6" - xtend "~4.0.1" - -"through@>=2.2.7 <3", through@X.X.X, through@^2.3.6: - version "2.3.8" - resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" - integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU= - -thunky@^1.0.2: - version "1.1.0" - resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" - integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== - -timers-browserify@^2.0.4: - version "2.0.12" - resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.12.tgz#44a45c11fbf407f34f97bccd1577c652361b00ee" - integrity sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ== - dependencies: - setimmediate "^1.0.4" - -timsort@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/timsort/-/timsort-0.3.0.tgz#405411a8e7e6339fe64db9a234de11dc31e02bd4" - integrity sha1-QFQRqOfmM5/mTbmiNN4R3DHgK9Q= - -tmp@0.0.30: - version "0.0.30" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.30.tgz#72419d4a8be7d6ce75148fd8b324e593a711c2ed" - integrity sha1-ckGdSovn1s51FI/YsyTlk6cRwu0= - dependencies: - os-tmpdir "~1.0.1" - -tmp@0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" - integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ== - dependencies: - rimraf "^3.0.0" - -tmp@^0.0.33: - version "0.0.33" - resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" - integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== - dependencies: - os-tmpdir "~1.0.2" - -to-array@0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/to-array/-/to-array-0.1.4.tgz#17e6c11f73dd4f3d74cda7a4ff3238e9ad9bf890" - integrity sha1-F+bBH3PdTz10zaek/zI46a2b+JA= - -to-arraybuffer@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" - integrity sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M= - -to-fast-properties@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" - integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= - -to-object-path@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" - integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= - dependencies: - kind-of "^3.0.2" - -to-readable-stream@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz#ce0aa0c2f3df6adf852efb404a783e77c0475771" - integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== - -to-regex-range@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" - integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= - dependencies: - is-number "^3.0.0" - repeat-string "^1.6.1" - -to-regex-range@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" - integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== - dependencies: - is-number "^7.0.0" - -to-regex@^3.0.1, to-regex@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" - integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== - dependencies: - define-property "^2.0.2" - extend-shallow "^3.0.2" - regex-not "^1.0.2" - safe-regex "^1.1.0" - -toidentifier@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.0.tgz#7e1be3470f1e77948bc43d94a3c8f4d7752ba553" - integrity sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw== - -tough-cookie@~2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2" - integrity sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g== - dependencies: - psl "^1.1.28" - punycode "^2.1.1" - -tree-kill@1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" - integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== - -ts-node@8.10.2: - version "8.10.2" - resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.10.2.tgz#eee03764633b1234ddd37f8db9ec10b75ec7fb8d" - integrity sha512-ISJJGgkIpDdBhWVu3jufsWpK3Rzo7bdiIXJjQc0ynKxVOVcg2oIrf2H2cejminGrptVc6q6/uynAHNCuWGbpVA== - dependencies: - arg "^4.1.0" - diff "^4.0.1" - make-error "^1.1.1" - source-map-support "^0.5.17" - yn "3.1.1" - -ts-pnp@^1.1.6: - version "1.2.0" - resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.2.0.tgz#a500ad084b0798f1c3071af391e65912c86bca92" - integrity sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw== - -tslib@2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.3.tgz#8e0741ac45fc0c226e58a17bfc3e64b9bc6ca61c" - integrity sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ== - -tslib@^1.10.0, tslib@^1.13.0, tslib@^1.8.1, tslib@^1.9.0: - version "1.14.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" - integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== - -tslib@^2.0.0, tslib@^2.0.1: - version "2.1.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a" - integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A== - -tslint@~6.1.3: - version "6.1.3" - resolved "https://registry.yarnpkg.com/tslint/-/tslint-6.1.3.tgz#5c23b2eccc32487d5523bd3a470e9aa31789d904" - integrity sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg== - dependencies: - "@babel/code-frame" "^7.0.0" - builtin-modules "^1.1.1" - chalk "^2.3.0" - commander "^2.12.1" - diff "^4.0.1" - glob "^7.1.1" - js-yaml "^3.13.1" - minimatch "^3.0.4" - mkdirp "^0.5.3" - resolve "^1.3.2" - semver "^5.3.0" - tslib "^1.13.0" - tsutils "^2.29.0" - -tsutils@^2.29.0: - version "2.29.0" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-2.29.0.tgz#32b488501467acbedd4b85498673a0812aca0b99" - integrity sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA== - dependencies: - tslib "^1.8.1" - -tty-browserify@0.0.0: - version "0.0.0" - resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" - integrity sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY= - -tunnel-agent@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" - integrity sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= - dependencies: - safe-buffer "^5.0.1" - -tunnel@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz#72f1314b34a5b192db012324df2cc587ca47f92c" - integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg== - -tweetnacl@^0.14.3, tweetnacl@~0.14.0: - version "0.14.5" - resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" - integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q= - -type-fest@^0.11.0: - version "0.11.0" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1" - integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ== - -type-fest@^0.13.1: - version "0.13.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.13.1.tgz#0172cb5bce80b0bd542ea348db50c7e21834d934" - integrity sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg== - -type-is@~1.6.17, type-is@~1.6.18: - version "1.6.18" - resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" - integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== - dependencies: - media-typer "0.3.0" - mime-types "~2.1.24" - -type@^1.0.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" - integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== - -type@^2.0.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/type/-/type-2.5.0.tgz#0a2e78c2e77907b252abe5f298c1b01c63f0db3d" - integrity sha512-180WMDQaIMm3+7hGXWf12GtdniDEy7nYcyFMKJn/eZz/6tSLXrUN9V0wKSbMjej0I1WHWbpREDEKHtqPQa9NNw== - -typedarray@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" - integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= - -typescript@4.0.5: - version "4.0.5" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.0.5.tgz#ae9dddfd1069f1cb5beb3ef3b2170dd7c1332389" - integrity sha512-ywmr/VrTVCmNTJ6iV2LwIrfG1P+lv6luD8sUJs+2eI9NLGigaN+nUQc13iHqisq7bra9lnmUSYqbJvegraBOPQ== - -ua-parser-js@0.7.21: - version "0.7.21" - resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.21.tgz#853cf9ce93f642f67174273cc34565ae6f308777" - integrity sha512-+O8/qh/Qj8CgC6eYBVBykMrNtp5Gebn4dlGD/kKXVkJNDwyrAwSIqwz8CDf+tsAIWVycKcku6gIXJ0qwx/ZXaQ== - -unbox-primitive@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.0.tgz#eeacbc4affa28e9b3d36b5eaeccc50b3251b1d3f" - integrity sha512-P/51NX+JXyxK/aigg1/ZgyccdAxm5K1+n8+tvqSntjOivPt19gvm1VC49RWYetsiub8WViUchdxl/KWHHB0kzA== - dependencies: - function-bind "^1.1.1" - has-bigints "^1.0.0" - has-symbols "^1.0.0" - which-boxed-primitive "^1.0.1" - -unicode-canonical-property-names-ecmascript@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-1.0.4.tgz#2619800c4c825800efdd8343af7dd9933cbe2818" - integrity sha512-jDrNnXWHd4oHiTZnx/ZG7gtUTVp+gCcTTKr8L0HjlwphROEW3+Him+IpvC+xcJEFegapiMZyZe02CyuOnRmbnQ== - -unicode-match-property-ecmascript@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/unicode-match-property-ecmascript/-/unicode-match-property-ecmascript-1.0.4.tgz#8ed2a32569961bce9227d09cd3ffbb8fed5f020c" - integrity sha512-L4Qoh15vTfntsn4P1zqnHulG0LdXgjSO035fEpdtp6YxXhMT51Q6vgM5lYdG/5X3MjS+k/Y9Xw4SFCY9IkR0rg== - dependencies: - unicode-canonical-property-names-ecmascript "^1.0.4" - unicode-property-aliases-ecmascript "^1.0.4" - -unicode-match-property-value-ecmascript@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz#0d91f600eeeb3096aa962b1d6fc88876e64ea531" - integrity sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ== - -unicode-property-aliases-ecmascript@^1.0.4: - version "1.1.0" - resolved "https://registry.yarnpkg.com/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz#dd57a99f6207bedff4628abefb94c50db941c8f4" - integrity sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg== - -union-value@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" - integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== - dependencies: - arr-union "^3.1.0" - get-value "^2.0.6" - is-extendable "^0.1.1" - set-value "^2.0.1" - -uniq@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/uniq/-/uniq-1.0.1.tgz#b31c5ae8254844a3a8281541ce2b04b865a734ff" - integrity sha1-sxxa6CVIRKOoKBVBzisEuGWnNP8= - -uniqs@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02" - integrity sha1-/+3ks2slKQaW5uFl1KWe25mOawI= - -unique-filename@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-1.1.1.tgz#1d69769369ada0583103a1e6ae87681b56573230" - integrity sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ== - dependencies: - unique-slug "^2.0.0" - -unique-slug@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-2.0.2.tgz#baabce91083fc64e945b0f3ad613e264f7cd4e6c" - integrity sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w== - dependencies: - imurmurhash "^0.1.4" - -universal-analytics@0.4.23: - version "0.4.23" - resolved "https://registry.yarnpkg.com/universal-analytics/-/universal-analytics-0.4.23.tgz#d915e676850c25c4156762471bdd7cf2eaaca8ac" - integrity sha512-lgMIH7XBI6OgYn1woDEmxhGdj8yDefMKg7GkWdeATAlQZFrMrNyxSkpDzY57iY0/6fdlzTbBV03OawvvzG+q7A== - dependencies: - debug "^4.1.1" - request "^2.88.2" - uuid "^3.0.0" - -universalify@^0.1.0: - version "0.1.2" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" - integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== - -unpipe@1.0.0, unpipe@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" - integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= - -unquote@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/unquote/-/unquote-1.1.1.tgz#8fded7324ec6e88a0ff8b905e7c098cdc086d544" - integrity sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ= - -unset-value@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" - integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= - dependencies: - has-value "^0.3.1" - isobject "^3.0.0" - -upath@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894" - integrity sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg== - -uri-js@^4.2.2: - version "4.4.1" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" - integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== - dependencies: - punycode "^2.1.0" - -urix@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" - integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= - -url-parse-lax@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c" - integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww= - dependencies: - prepend-http "^2.0.0" - -url-parse@^1.4.3: - version "1.5.1" - resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.5.1.tgz#d5fa9890af8a5e1f274a2c98376510f6425f6e3b" - integrity sha512-HOfCOUJt7iSYzEx/UqgtwKRMC6EU91NFhsCHMv9oM03VJcVo2Qrp8T8kI9D7amFf1cu+/3CEhgb3rF9zL7k85Q== - dependencies: - querystringify "^2.1.1" - requires-port "^1.0.0" - -url@^0.11.0: - version "0.11.0" - resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1" - integrity sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE= - dependencies: - punycode "1.3.2" - querystring "0.2.0" - -use@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" - integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== - -util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= - -util.promisify@~1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/util.promisify/-/util.promisify-1.0.1.tgz#6baf7774b80eeb0f7520d8b81d07982a59abbaee" - integrity sha512-g9JpC/3He3bm38zsLupWryXHoEcS22YHthuPQSJdMy6KNrzIRzWqcsHzD/WUnqe45whVou4VIsPew37DoXWNrA== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.17.2" - has-symbols "^1.0.1" - object.getownpropertydescriptors "^2.1.0" - -util@0.10.3: - version "0.10.3" - resolved "https://registry.yarnpkg.com/util/-/util-0.10.3.tgz#7afb1afe50805246489e3db7fe0ed379336ac0f9" - integrity sha1-evsa/lCAUkZInj23/g7TeTNqwPk= - dependencies: - inherits "2.0.1" - -util@^0.11.0: - version "0.11.1" - resolved "https://registry.yarnpkg.com/util/-/util-0.11.1.tgz#3236733720ec64bb27f6e26f421aaa2e1b588d61" - integrity sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ== - dependencies: - inherits "2.0.3" - -utils-merge@1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" - integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM= - -uuid@8.3.1: - version "8.3.1" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.1.tgz#2ba2e6ca000da60fce5a196954ab241131e05a31" - integrity sha512-FOmRr+FmWEIG8uhZv6C2bTgEVXsHk08kE7mPlrBbEe+c3r9pjceVPgupIfNIhc4yx55H69OXANrUaSuu9eInKg== - -uuid@^3.0.0, uuid@^3.3.2, uuid@^3.4.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" - integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A== - -validate-npm-package-license@^3.0.1: - version "3.0.4" - resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" - integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== - dependencies: - spdx-correct "^3.0.0" - spdx-expression-parse "^3.0.0" - -validate-npm-package-name@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/validate-npm-package-name/-/validate-npm-package-name-3.0.0.tgz#5fa912d81eb7d0c74afc140de7317f0ca7df437e" - integrity sha1-X6kS2B630MdK/BQN5zF/DKffQ34= - dependencies: - builtins "^1.0.3" - -vary@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" - integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= - -vendors@^1.0.0: - version "1.0.4" - resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.4.tgz#e2b800a53e7a29b93506c3cf41100d16c4c4ad8e" - integrity sha512-/juG65kTL4Cy2su4P8HjtkTxk6VmJDiOPBufWniqQ6wknac6jNiXS9vU+hO3wgusiyqWlzTbVHi0dyJqRONg3w== - -verror@1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/verror/-/verror-1.10.0.tgz#3a105ca17053af55d6e270c1f8288682e18da400" - integrity sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA= - dependencies: - assert-plus "^1.0.0" - core-util-is "1.0.2" - extsprintf "^1.2.0" - -vm-browserify@^1.0.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" - integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== - -void-elements@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec" - integrity sha1-wGavtYK7HLQSjWDqkjkulNXp2+w= - -watchpack-chokidar2@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/watchpack-chokidar2/-/watchpack-chokidar2-2.0.1.tgz#38500072ee6ece66f3769936950ea1771be1c957" - integrity sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww== - dependencies: - chokidar "^2.1.8" - -watchpack@^1.7.4: - version "1.7.5" - resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.7.5.tgz#1267e6c55e0b9b5be44c2023aed5437a2c26c453" - integrity sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ== - dependencies: - graceful-fs "^4.1.2" - neo-async "^2.5.0" - optionalDependencies: - chokidar "^3.4.1" - watchpack-chokidar2 "^2.0.1" - -wbuf@^1.1.0, wbuf@^1.7.3: - version "1.7.3" - resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df" - integrity sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA== - dependencies: - minimalistic-assert "^1.0.0" - -wcwidth@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" - integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g= - dependencies: - defaults "^1.0.3" - -webdriver-js-extender@2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/webdriver-js-extender/-/webdriver-js-extender-2.1.0.tgz#57d7a93c00db4cc8d556e4d3db4b5db0a80c3bb7" - integrity sha512-lcUKrjbBfCK6MNsh7xaY2UAUmZwe+/ib03AjVOpFobX4O7+83BUveSrLfU0Qsyb1DaKJdQRbuU+kM9aZ6QUhiQ== - dependencies: - "@types/selenium-webdriver" "^3.0.0" - selenium-webdriver "^3.0.1" - -webdriver-manager@^12.1.7: - version "12.1.8" - resolved "https://registry.yarnpkg.com/webdriver-manager/-/webdriver-manager-12.1.8.tgz#5e70e73eaaf53a0767d5745270addafbc5905fd4" - integrity sha512-qJR36SXG2VwKugPcdwhaqcLQOD7r8P2Xiv9sfNbfZrKBnX243iAkOueX1yAmeNgIKhJ3YAT/F2gq6IiEZzahsg== - dependencies: - adm-zip "^0.4.9" - chalk "^1.1.1" - del "^2.2.0" - glob "^7.0.3" - ini "^1.3.4" - minimist "^1.2.0" - q "^1.4.1" - request "^2.87.0" - rimraf "^2.5.2" - semver "^5.3.0" - xml2js "^0.4.17" - -webpack-dev-middleware@3.7.2: - version "3.7.2" - resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.2.tgz#0019c3db716e3fa5cecbf64f2ab88a74bab331f3" - integrity sha512-1xC42LxbYoqLNAhV6YzTYacicgMZQTqRd27Sim9wn5hJrX3I5nxYy1SxSd4+gjUFsz1dQFj+yEe6zEVmSkeJjw== - dependencies: - memory-fs "^0.4.1" - mime "^2.4.4" - mkdirp "^0.5.1" - range-parser "^1.2.1" - webpack-log "^2.0.0" - -webpack-dev-middleware@^3.7.2: - version "3.7.3" - resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.3.tgz#0639372b143262e2b84ab95d3b91a7597061c2c5" - integrity sha512-djelc/zGiz9nZj/U7PTBi2ViorGJXEWo/3ltkPbDyxCXhhEXkW0ce99falaok4TPj+AsxLiXJR0EBOb0zh9fKQ== - dependencies: - memory-fs "^0.4.1" - mime "^2.4.4" - mkdirp "^0.5.1" - range-parser "^1.2.1" - webpack-log "^2.0.0" - -webpack-dev-server@3.11.0: - version "3.11.0" - resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.11.0.tgz#8f154a3bce1bcfd1cc618ef4e703278855e7ff8c" - integrity sha512-PUxZ+oSTxogFQgkTtFndEtJIPNmml7ExwufBZ9L2/Xyyd5PnOL5UreWe5ZT7IU25DSdykL9p1MLQzmLh2ljSeg== - dependencies: - ansi-html "0.0.7" - bonjour "^3.5.0" - chokidar "^2.1.8" - compression "^1.7.4" - connect-history-api-fallback "^1.6.0" - debug "^4.1.1" - del "^4.1.1" - express "^4.17.1" - html-entities "^1.3.1" - http-proxy-middleware "0.19.1" - import-local "^2.0.0" - internal-ip "^4.3.0" - ip "^1.1.5" - is-absolute-url "^3.0.3" - killable "^1.0.1" - loglevel "^1.6.8" - opn "^5.5.0" - p-retry "^3.0.1" - portfinder "^1.0.26" - schema-utils "^1.0.0" - selfsigned "^1.10.7" - semver "^6.3.0" - serve-index "^1.9.1" - sockjs "0.3.20" - sockjs-client "1.4.0" - spdy "^4.0.2" - strip-ansi "^3.0.1" - supports-color "^6.1.0" - url "^0.11.0" - webpack-dev-middleware "^3.7.2" - webpack-log "^2.0.0" - ws "^6.2.1" - yargs "^13.3.2" - -webpack-log@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/webpack-log/-/webpack-log-2.0.0.tgz#5b7928e0637593f119d32f6227c1e0ac31e1b47f" - integrity sha512-cX8G2vR/85UYG59FgkoMamwHUIkSSlV3bBMRsbxVXVUk2j6NleCKjQ/WE9eYg9WY4w25O9w8wKP4rzNZFmUcUg== - dependencies: - ansi-colors "^3.0.0" - uuid "^3.3.2" - -webpack-merge@5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.2.0.tgz#31cbcc954f8f89cd4b06ca8d97a38549f7f3f0c9" - integrity sha512-QBglJBg5+lItm3/Lopv8KDDK01+hjdg2azEwi/4vKJ8ZmGPdtJsTpjtNNOW3a4WiqzXdCATtTudOZJngE7RKkA== - dependencies: - clone-deep "^4.0.1" - wildcard "^2.0.0" - -webpack-sources@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-2.0.1.tgz#1467f6e692ddce91e88b8044c44347b1087bbd4f" - integrity sha512-A9oYz7ANQBK5EN19rUXbvNgfdfZf5U2gP0769OXsj9CvYkCR6OHOsd6OKyEy4H38GGxpsQPKIL83NC64QY6Xmw== - dependencies: - source-list-map "^2.0.1" - source-map "^0.6.1" - -webpack-sources@^1.1.0, webpack-sources@^1.2.0, webpack-sources@^1.3.0, webpack-sources@^1.4.0, webpack-sources@^1.4.1, webpack-sources@^1.4.3: - version "1.4.3" - resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-1.4.3.tgz#eedd8ec0b928fbf1cbfe994e22d2d890f330a933" - integrity sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ== - dependencies: - source-list-map "^2.0.0" - source-map "~0.6.1" - -webpack-subresource-integrity@1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/webpack-subresource-integrity/-/webpack-subresource-integrity-1.5.1.tgz#6f44ea99987266b70c4ec42ac51064d33e982277" - integrity sha512-uekbQ93PZ9e7BFB8Hl9cFIVYQyQqiXp2ExKk9Zv+qZfH/zHXHrCFAfw1VW0+NqWbTWrs/HnuDrto3+tiPXh//Q== - dependencies: - webpack-sources "^1.3.0" - -webpack@4.44.2: - version "4.44.2" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.44.2.tgz#6bfe2b0af055c8b2d1e90ed2cd9363f841266b72" - integrity sha512-6KJVGlCxYdISyurpQ0IPTklv+DULv05rs2hseIXer6D7KrUicRDLFb4IUM1S6LUAKypPM/nSiVSuv8jHu1m3/Q== - dependencies: - "@webassemblyjs/ast" "1.9.0" - "@webassemblyjs/helper-module-context" "1.9.0" - "@webassemblyjs/wasm-edit" "1.9.0" - "@webassemblyjs/wasm-parser" "1.9.0" - acorn "^6.4.1" - ajv "^6.10.2" - ajv-keywords "^3.4.1" - chrome-trace-event "^1.0.2" - enhanced-resolve "^4.3.0" - eslint-scope "^4.0.3" - json-parse-better-errors "^1.0.2" - loader-runner "^2.4.0" - loader-utils "^1.2.3" - memory-fs "^0.4.1" - micromatch "^3.1.10" - mkdirp "^0.5.3" - neo-async "^2.6.1" - node-libs-browser "^2.2.1" - schema-utils "^1.0.0" - tapable "^1.1.3" - terser-webpack-plugin "^1.4.3" - watchpack "^1.7.4" - webpack-sources "^1.4.1" - -websocket-driver@0.6.5: - version "0.6.5" - resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.6.5.tgz#5cb2556ceb85f4373c6d8238aa691c8454e13a36" - integrity sha1-XLJVbOuF9Dc8bYI4qmkchFThOjY= - dependencies: - websocket-extensions ">=0.1.1" - -websocket-driver@>=0.5.1: - version "0.7.4" - resolved "https://registry.yarnpkg.com/websocket-driver/-/websocket-driver-0.7.4.tgz#89ad5295bbf64b480abcba31e4953aca706f5760" - integrity sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg== - dependencies: - http-parser-js ">=0.5.1" - safe-buffer ">=5.1.0" - websocket-extensions ">=0.1.1" - -websocket-extensions@>=0.1.1: - version "0.1.4" - resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" - integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== - -whatwg-mimetype@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" - integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== - -which-boxed-primitive@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" - integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== - 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" - -which-module@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" - integrity sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho= - -which@^1.2.1, which@^1.2.9, which@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" - integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== - dependencies: - isexe "^2.0.0" - -wildcard@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec" - integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw== - -worker-farm@^1.7.0: - version "1.7.0" - resolved "https://registry.yarnpkg.com/worker-farm/-/worker-farm-1.7.0.tgz#26a94c5391bbca926152002f69b84a4bf772e5a8" - integrity sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw== - dependencies: - errno "~0.1.7" - -worker-plugin@5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/worker-plugin/-/worker-plugin-5.0.0.tgz#113b5fe1f4a5d6a957cecd29915bedafd70bb537" - integrity sha512-AXMUstURCxDD6yGam2r4E34aJg6kW85IiaeX72hi+I1cxyaMUtrvVY6sbfpGKAj5e7f68Acl62BjQF5aOOx2IQ== - dependencies: - loader-utils "^1.1.0" - -wrap-ansi@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" - integrity sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q== - dependencies: - ansi-styles "^3.2.0" - string-width "^3.0.0" - strip-ansi "^5.0.0" - -wrap-ansi@^6.2.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" - integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= - -ws@^6.2.1: - version "6.2.1" - resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb" - integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA== - dependencies: - async-limiter "~1.0.0" - -ws@~7.4.2: - version "7.4.4" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.4.tgz#383bc9742cb202292c9077ceab6f6047b17f2d59" - integrity sha512-Qm8k8ojNQIMx7S+Zp8u/uHOx7Qazv3Yv4q68MiWWWOJhiwG5W3x7iqmRtJo8xxrciZUY4vRxUTJCKuRnF28ZZw== - -xml2js@^0.4.17: - version "0.4.23" - resolved "https://registry.yarnpkg.com/xml2js/-/xml2js-0.4.23.tgz#a0c69516752421eb2ac758ee4d4ccf58843eac66" - integrity sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug== - dependencies: - sax ">=0.6.0" - xmlbuilder "~11.0.0" - -xmlbuilder@~11.0.0: - version "11.0.1" - resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-11.0.1.tgz#be9bae1c8a046e76b31127726347d0ad7002beb3" - integrity sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA== - -xmlhttprequest-ssl@~1.5.4: - version "1.5.5" - resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.5.5.tgz#c2876b06168aadc40e57d97e81191ac8f4398b3e" - integrity sha1-wodrBhaKrcQOV9l+gRkayPQ5iz4= - -xtend@^4.0.0, xtend@~4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" - integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== - -y18n@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.1.tgz#8db2b83c31c5d75099bb890b23f3094891e247d4" - integrity sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ== - -y18n@^5.0.5: - version "5.0.5" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.5.tgz#8769ec08d03b1ea2df2500acef561743bbb9ab18" - integrity sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg== - -yallist@^3.0.0, yallist@^3.0.2, yallist@^3.0.3: - version "3.1.1" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" - integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== - -yallist@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" - integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== - -yaml@^1.10.0: - version "1.10.1" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.1.tgz#bb13d805ed104fba38f533570f3441027eeeca22" - integrity sha512-z/asvd+V08l1ywhaemZVirCwjdzLo6O1/0j2JbYCsGjiezupNQqjs5IIPyNtctbHjPEckqzVGd4jvpU5Lr25vQ== - -yargs-parser@^13.1.2: - version "13.1.2" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38" - integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" - -yargs-parser@^18.1.2: - version "18.1.3" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0" - integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ== - dependencies: - camelcase "^5.0.0" - decamelize "^1.2.0" - -yargs-parser@^20.2.2: - version "20.2.7" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.7.tgz#61df85c113edfb5a7a4e36eb8aa60ef423cbc90a" - integrity sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw== - -yargs@^13.3.2: - version "13.3.2" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-13.3.2.tgz#ad7ffefec1aa59565ac915f82dccb38a9c31a2dd" - integrity sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw== - dependencies: - cliui "^5.0.0" - find-up "^3.0.0" - get-caller-file "^2.0.1" - require-directory "^2.1.1" - require-main-filename "^2.0.0" - set-blocking "^2.0.0" - string-width "^3.0.0" - which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^13.1.2" - -yargs@^15.3.1: - version "15.4.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-15.4.1.tgz#0d87a16de01aee9d8bec2bfbf74f67851730f4f8" - integrity sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A== - dependencies: - cliui "^6.0.0" - decamelize "^1.2.0" - find-up "^4.1.0" - get-caller-file "^2.0.1" - require-directory "^2.1.1" - require-main-filename "^2.0.0" - set-blocking "^2.0.0" - string-width "^4.2.0" - which-module "^2.0.0" - y18n "^4.0.0" - yargs-parser "^18.1.2" - -yargs@^16.1.1: - version "16.2.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" - integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== - 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" - -yauzl@^2.10.0: - version "2.10.0" - resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" - integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= - dependencies: - buffer-crc32 "~0.2.3" - fd-slicer "~1.1.0" - -yeast@0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419" - integrity sha1-AI4G2AlDIMNy28L47XagymyKxBk= - -yn@3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" - integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== - -yocto-queue@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" - integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== - -zone.js@0.10.3, zone.js@~0.10.3: - version "0.10.3" - resolved "https://registry.yarnpkg.com/zone.js/-/zone.js-0.10.3.tgz#3e5e4da03c607c9dcd92e37dd35687a14a140c16" - integrity sha512-LXVLVEq0NNOqK/fLJo3d0kfzd4sxwn2/h67/02pjCjfKDxgx1i9QqpvtHD8CrBnSSwMw5+dy11O7FRX5mkO7Cg== diff --git a/vripper-ui/.angulardoc.json b/vripper-web-ui/.angulardoc.json similarity index 100% rename from vripper-ui/.angulardoc.json rename to vripper-web-ui/.angulardoc.json diff --git a/vripper-ui/.editorconfig b/vripper-web-ui/.editorconfig similarity index 100% rename from vripper-ui/.editorconfig rename to vripper-web-ui/.editorconfig diff --git a/vripper-ui/.gitignore b/vripper-web-ui/.gitignore similarity index 100% rename from vripper-ui/.gitignore rename to vripper-web-ui/.gitignore diff --git a/vripper-ui/.prettierrc b/vripper-web-ui/.prettierrc similarity index 100% rename from vripper-ui/.prettierrc rename to vripper-web-ui/.prettierrc diff --git a/vripper-ui/README.md b/vripper-web-ui/README.md similarity index 100% rename from vripper-ui/README.md rename to vripper-web-ui/README.md diff --git a/vripper-ui/angular.json b/vripper-web-ui/angular.json similarity index 53% rename from vripper-ui/angular.json rename to vripper-web-ui/angular.json index 348943ce..98a47f5c 100644 --- a/vripper-ui/angular.json +++ b/vripper-web-ui/angular.json @@ -17,12 +17,14 @@ "build": { "builder": "@angular-devkit/build-angular:browser", "options": { - "aot": true, "outputPath": "dist/vripper-ui", "index": "src/index.html", "main": "src/main.ts", - "polyfills": "src/polyfills.ts", - "tsConfig": "src/tsconfig.app.json", + "polyfills": [ + "zone.js" + ], + "tsConfig": "tsconfig.app.json", + "inlineStyleLanguage": "scss", "assets": [ "src/favicon.ico", "src/assets", @@ -41,20 +43,6 @@ }, "configurations": { "production": { - "fileReplacements": [ - { - "replace": "src/environments/environment.ts", - "with": "src/environments/environment.prod.ts" - } - ], - "optimization": true, - "outputHashing": "all", - "sourceMap": false, - "namedChunks": false, - "aot": true, - "extractLicenses": true, - "vendorChunk": false, - "buildOptimizer": true, "budgets": [ { "type": "initial", @@ -66,19 +54,29 @@ "maximumWarning": "6kb" } ] + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true } - } + }, + "defaultConfiguration": "production" }, "serve": { "builder": "@angular-devkit/build-angular:dev-server", - "options": { - "browserTarget": "vripper-ui:build" - }, "configurations": { "production": { "browserTarget": "vripper-ui:build:production" + }, + "development": { + "browserTarget": "vripper-ui:build:development" } - } + }, + "defaultConfiguration": "development" }, "extract-i18n": { "builder": "@angular-devkit/build-angular:extract-i18n", @@ -89,10 +87,12 @@ "test": { "builder": "@angular-devkit/build-angular:karma", "options": { - "main": "src/test.ts", - "polyfills": "src/polyfills.ts", - "tsConfig": "src/tsconfig.spec.json", - "karmaConfig": "src/karma.conf.js", + "polyfills": [ + "zone.js", + "zone.js/testing" + ], + "tsConfig": "tsconfig.spec.json", + "inlineStyleLanguage": "scss", "styles": [ "src/styles.scss" ], @@ -100,51 +100,11 @@ "src/favicon.ico", "src/assets", { "glob": "mdi.svg", "input": "./node_modules/@mdi/angular-material", "output": "./assets" } - ] - } - }, - "lint": { - "builder": "@angular-devkit/build-angular:tslint", - "options": { - "tsConfig": [ - "src/tsconfig.app.json", - "src/tsconfig.spec.json" ], - "exclude": [ - "**/node_modules/**" - ] - } - } - } - }, - "vripper-ui-e2e": { - "root": "e2e/", - "projectType": "application", - "prefix": "", - "architect": { - "e2e": { - "builder": "@angular-devkit/build-angular:protractor", - "options": { - "protractorConfig": "e2e/protractor.conf.js", - "devServerTarget": "vripper-ui:serve" - }, - "configurations": { - "production": { - "devServerTarget": "vripper-ui:serve:production" - } - } - }, - "lint": { - "builder": "@angular-devkit/build-angular:tslint", - "options": { - "tsConfig": "e2e/tsconfig.e2e.json", - "exclude": [ - "**/node_modules/**" - ] + "scripts": [] } } } } - }, - "defaultProject": "vripper-ui" + } } diff --git a/vripper-web-ui/package.json b/vripper-web-ui/package.json new file mode 100644 index 00000000..281025dc --- /dev/null +++ b/vripper-web-ui/package.json @@ -0,0 +1,47 @@ +{ + "name": "vripper-ui", + "version": "3.5.4", + "scripts": { + "ng": "ng", + "start": "ng serve", + "build": "ng build", + "watch": "ng build --watch --configuration development", + "test": "ng test" + }, + "private": true, + "dependencies": { + "@angular/animations": "^15.2.0", + "@angular/cdk": "^15.2.0", + "@angular/common": "^15.2.0", + "@angular/compiler": "^15.2.0", + "@angular/core": "^15.2.0", + "@angular/flex-layout": "15.0.0-beta.42", + "@angular/forms": "^15.2.0", + "@angular/material": "^15.2.0", + "@angular/platform-browser": "^15.2.0", + "@angular/platform-browser-dynamic": "^15.2.0", + "@angular/router": "^15.2.0", + "@mdi/angular-material": "^7.2.96", + "@stomp/rx-stomp": "^2.0.0", + "@stomp/stompjs": "^7.0.0", + "uuid": "^9.0.0", + "ag-grid-angular": "^29.1.0", + "ag-grid-community": "^29.1.0", + "rxjs": "~7.8.0", + "tslib": "^2.3.0", + "zone.js": "~0.12.0" + }, + "devDependencies": { + "@angular-devkit/build-angular": "^15.2.4", + "@angular/cli": "~15.2.4", + "@angular/compiler-cli": "^15.2.0", + "@types/jasmine": "~4.3.0", + "jasmine-core": "~4.5.0", + "karma": "~6.4.0", + "karma-chrome-launcher": "~3.1.0", + "karma-coverage": "~2.2.0", + "karma-jasmine": "~5.1.0", + "karma-jasmine-html-reporter": "~2.0.0", + "typescript": "~4.9.4" + } +} diff --git a/vripper-ui/pom.xml b/vripper-web-ui/pom.xml similarity index 99% rename from vripper-ui/pom.xml rename to vripper-web-ui/pom.xml index f6aa4cb4..ae5906be 100644 --- a/vripper-ui/pom.xml +++ b/vripper-web-ui/pom.xml @@ -3,7 +3,7 @@ xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> 4.0.0 - tn.mnlr + me.mnlr vripper 3.5.4 diff --git a/vripper-ui/src/.browserslistrc b/vripper-web-ui/src/.browserslistrc similarity index 100% rename from vripper-ui/src/.browserslistrc rename to vripper-web-ui/src/.browserslistrc diff --git a/vripper-web-ui/src/app/about/about-component.component-scss-theme.scss b/vripper-web-ui/src/app/about/about-component.component-scss-theme.scss new file mode 100644 index 00000000..fbdfef74 --- /dev/null +++ b/vripper-web-ui/src/app/about/about-component.component-scss-theme.scss @@ -0,0 +1,11 @@ +@use 'sass:map'; +@use '@angular/material' as mat; + +@mixin about-component-theme($theme) { + $theme-color: mat.get-color-config($theme); + $primary: map.get($theme-color, 'primary'); + + .about-link, .about-icon { + color: mat.get-color-from-palette($primary, '900-contrast'); + } +} diff --git a/vripper-ui/src/app/about/about-component.html b/vripper-web-ui/src/app/about/about-component.html similarity index 100% rename from vripper-ui/src/app/about/about-component.html rename to vripper-web-ui/src/app/about/about-component.html diff --git a/vripper-ui/src/app/about/about-component.scss b/vripper-web-ui/src/app/about/about-component.scss similarity index 100% rename from vripper-ui/src/app/about/about-component.scss rename to vripper-web-ui/src/app/about/about-component.scss diff --git a/vripper-ui/src/app/about/about-component.ts b/vripper-web-ui/src/app/about/about-component.ts similarity index 59% rename from vripper-ui/src/app/about/about-component.ts rename to vripper-web-ui/src/app/about/about-component.ts index 2f406409..08cd3a9e 100644 --- a/vripper-ui/src/app/about/about-component.ts +++ b/vripper-web-ui/src/app/about/about-component.ts @@ -1,6 +1,5 @@ import {ChangeDetectionStrategy, Component} from '@angular/core'; import {environment} from '../../environments/environment'; -import {ElectronService} from 'ngx-electron'; @Component({ selector: 'app-about', @@ -12,16 +11,7 @@ export class AboutComponent { version = environment.version; - constructor( - public electronService: ElectronService - ) { - } - goTo(url: string) { - if (this.electronService.isElectronApp) { - this.electronService.shell.openExternal(url); - } else { - window.open(url, '_blank'); - } + window.open(url, '_blank'); } } diff --git a/vripper-ui/src/app/app-routing.module.ts b/vripper-web-ui/src/app/app-routing.module.ts similarity index 85% rename from vripper-ui/src/app/app-routing.module.ts rename to vripper-web-ui/src/app/app-routing.module.ts index 78530df8..8c95cdf1 100644 --- a/vripper-ui/src/app/app-routing.module.ts +++ b/vripper-web-ui/src/app/app-routing.module.ts @@ -10,7 +10,7 @@ const routes: Routes = [ @NgModule({ imports: [ - RouterModule.forRoot(routes, {relativeLinkResolution: 'legacy'}) + RouterModule.forRoot(routes) ], exports: [ RouterModule diff --git a/vripper-ui/src/app/app.component.html b/vripper-web-ui/src/app/app.component.html similarity index 86% rename from vripper-ui/src/app/app.component.html rename to vripper-web-ui/src/app/app.component.html index 8d851690..ea7e939e 100644 --- a/vripper-ui/src/app/app.component.html +++ b/vripper-web-ui/src/app/app.component.html @@ -1,4 +1,4 @@ -
diff --git a/vripper-ui/src/app/app.component.scss b/vripper-web-ui/src/app/app.component.scss similarity index 96% rename from vripper-ui/src/app/app.component.scss rename to vripper-web-ui/src/app/app.component.scss index 6a67985f..20c6a8a7 100644 --- a/vripper-ui/src/app/app.component.scss +++ b/vripper-web-ui/src/app/app.component.scss @@ -1,3 +1,9 @@ +:host { + box-sizing: border-box; + display: block; + height: 100%; +} + .overlay-icon { font-size: 64px; width: 64px; diff --git a/vripper-web-ui/src/app/app.component.scss-theme.scss b/vripper-web-ui/src/app/app.component.scss-theme.scss new file mode 100644 index 00000000..81229175 --- /dev/null +++ b/vripper-web-ui/src/app/app.component.scss-theme.scss @@ -0,0 +1,11 @@ +@use 'sass:map'; +@use '@angular/material' as mat; + +@mixin app-component-theme($theme) { + $theme-color: mat.get-color-config($theme); + $primary: map.get($theme-color, 'primary'); + + button.add-button { + background-color: mat.get-color-from-palette($primary, 900); + } +} diff --git a/vripper-ui/src/app/app.component.ts b/vripper-web-ui/src/app/app.component.ts similarity index 82% rename from vripper-ui/src/app/app.component.ts rename to vripper-web-ui/src/app/app.component.ts index 0c8fa65a..31994329 100644 --- a/vripper-ui/src/app/app.component.ts +++ b/vripper-web-ui/src/app/app.component.ts @@ -1,6 +1,4 @@ import {AppService} from './services/app.service'; -import {ClipboardService} from './services/clipboard.service'; -import {ElectronService} from 'ngx-electron'; import {AfterViewInit, ChangeDetectionStrategy, Component, NgZone, OnDestroy, Renderer2} from '@angular/core'; import {MatDialog} from '@angular/material/dialog'; import {BehaviorSubject, merge, Subject, Subscription} from 'rxjs'; @@ -14,21 +12,16 @@ import {RxStompState} from '@stomp/rx-stomp'; changeDetection: ChangeDetectionStrategy.OnPush }) export class AppComponent implements OnDestroy, AfterViewInit { - electron: Subject; loaded: Subject = new BehaviorSubject(false); private subscriptions: Subscription[] = []; constructor( private dialog: MatDialog, public ws: WsConnectionService, - public electronService: ElectronService, - private clipboardService: ClipboardService, private appService: AppService, private renderer: Renderer2, private ngZone: NgZone - ) { - this.electron = new BehaviorSubject(electronService.isElectronApp); - } + ) {} ngAfterViewInit() { this.appService.renderer = this.renderer; @@ -36,7 +29,6 @@ export class AppComponent implements OnDestroy, AfterViewInit { this.ws.state.subscribe(state => { if (state === RxStompState.OPEN) { this.ngZone.run(() => this.loaded.next(true)); - this.clipboardService.init(); this.subscriptions.push(merge(this.appService.loadTheme(), this.appService.loadSettings()).subscribe()); } else { this.ngZone.run(() => this.loaded.next(false)); diff --git a/vripper-ui/src/app/app.module.ts b/vripper-web-ui/src/app/app.module.ts similarity index 92% rename from vripper-ui/src/app/app.module.ts rename to vripper-web-ui/src/app/app.module.ts index 8b2a0bd8..d317046a 100644 --- a/vripper-ui/src/app/app.module.ts +++ b/vripper-web-ui/src/app/app.module.ts @@ -23,8 +23,6 @@ import {ScanComponent} from './scan/scan.component'; import {StatusBarComponent} from './status-bar/status-bar.component'; import {ToolbarComponent} from './toolbar/toolbar.component'; import {MultiPostGridComponent} from './multi-post-grid/multi-post-grid.component'; -import {AlternativeTitleComponent} from './posts/alternative-title/alternative-title.component'; -import {NgxElectronModule} from 'ngx-electron'; import {MatIconRegistry} from '@angular/material/icon'; import {DomSanitizer} from '@angular/platform-browser'; import {EventLogComponent} from './event-log/event-log.component'; @@ -47,7 +45,6 @@ import {AboutComponent} from './about/about-component'; ToolbarComponent, MultiPostGridComponent, PostContextMenuComponent, - AlternativeTitleComponent, EventLogComponent, EventLogMessageDialogComponent, AboutComponent @@ -61,8 +58,7 @@ import {AboutComponent} from './about/about-component'; AppRoutingModule, ReactiveFormsModule, AgGridModule, - OverlayModule, - NgxElectronModule + OverlayModule ], bootstrap: [AppComponent] }) diff --git a/vripper-ui/src/app/confirmation-component/confirmation-dialog.html b/vripper-web-ui/src/app/confirmation-component/confirmation-dialog.html similarity index 100% rename from vripper-ui/src/app/confirmation-component/confirmation-dialog.html rename to vripper-web-ui/src/app/confirmation-component/confirmation-dialog.html diff --git a/vripper-ui/src/app/confirmation-component/confirmation-dialog.ts b/vripper-web-ui/src/app/confirmation-component/confirmation-dialog.ts similarity index 100% rename from vripper-ui/src/app/confirmation-component/confirmation-dialog.ts rename to vripper-web-ui/src/app/confirmation-component/confirmation-dialog.ts diff --git a/vripper-ui/src/app/domain/credential.model.ts b/vripper-web-ui/src/app/domain/credential.model.ts similarity index 100% rename from vripper-ui/src/app/domain/credential.model.ts rename to vripper-web-ui/src/app/domain/credential.model.ts diff --git a/vripper-ui/src/app/domain/download-path.model.ts b/vripper-web-ui/src/app/domain/download-path.model.ts similarity index 100% rename from vripper-ui/src/app/domain/download-path.model.ts rename to vripper-web-ui/src/app/domain/download-path.model.ts diff --git a/vripper-ui/src/app/domain/event.model.ts b/vripper-web-ui/src/app/domain/event.model.ts similarity index 100% rename from vripper-ui/src/app/domain/event.model.ts rename to vripper-web-ui/src/app/domain/event.model.ts diff --git a/vripper-ui/src/app/domain/global-state.model.ts b/vripper-web-ui/src/app/domain/global-state.model.ts similarity index 54% rename from vripper-ui/src/app/domain/global-state.model.ts rename to vripper-web-ui/src/app/domain/global-state.model.ts index fa0e8c74..841981e4 100644 --- a/vripper-ui/src/app/domain/global-state.model.ts +++ b/vripper-web-ui/src/app/domain/global-state.model.ts @@ -1,4 +1,4 @@ export class GlobalState { - constructor(public running: number, public remaining: number, public error: number) { + constructor(public running: number, public remaining: number, public error: number, public loggedUser: string, public downloadSpeed: string) { } } diff --git a/vripper-ui/src/app/domain/multi-post.model.ts b/vripper-web-ui/src/app/domain/multi-post.model.ts similarity index 100% rename from vripper-ui/src/app/domain/multi-post.model.ts rename to vripper-web-ui/src/app/domain/multi-post.model.ts diff --git a/vripper-ui/src/app/domain/photo-model.ts b/vripper-web-ui/src/app/domain/photo-model.ts similarity index 100% rename from vripper-ui/src/app/domain/photo-model.ts rename to vripper-web-ui/src/app/domain/photo-model.ts diff --git a/vripper-ui/src/app/domain/post-id.model.ts b/vripper-web-ui/src/app/domain/post-id.model.ts similarity index 100% rename from vripper-ui/src/app/domain/post-id.model.ts rename to vripper-web-ui/src/app/domain/post-id.model.ts diff --git a/vripper-ui/src/app/domain/post-state.model.ts b/vripper-web-ui/src/app/domain/post-state.model.ts similarity index 77% rename from vripper-ui/src/app/domain/post-state.model.ts rename to vripper-web-ui/src/app/domain/post-state.model.ts index 78b5fc9f..7a79b2dd 100644 --- a/vripper-ui/src/app/domain/post-state.model.ts +++ b/vripper-web-ui/src/app/domain/post-state.model.ts @@ -1,16 +1,13 @@ export class Post { constructor( public postId: string, - public title: string, + public postTitle: string, public progress: number, public status: string, - public removed: boolean, public url: string, public done: number, public total: number, public hosts: string[], - public thanked: boolean, - public previews: string[], public metadata: Metadata, public addedOn: string, public rank: number diff --git a/vripper-ui/src/app/domain/remove-all-response.model.ts b/vripper-web-ui/src/app/domain/remove-all-response.model.ts similarity index 100% rename from vripper-ui/src/app/domain/remove-all-response.model.ts rename to vripper-web-ui/src/app/domain/remove-all-response.model.ts diff --git a/vripper-ui/src/app/domain/remove-response.model.ts b/vripper-web-ui/src/app/domain/remove-response.model.ts similarity index 100% rename from vripper-ui/src/app/domain/remove-response.model.ts rename to vripper-web-ui/src/app/domain/remove-response.model.ts diff --git a/vripper-ui/src/app/domain/rename-post.model.ts b/vripper-web-ui/src/app/domain/rename-post.model.ts similarity index 100% rename from vripper-ui/src/app/domain/rename-post.model.ts rename to vripper-web-ui/src/app/domain/rename-post.model.ts diff --git a/vripper-web-ui/src/app/domain/settings.model.ts b/vripper-web-ui/src/app/domain/settings.model.ts new file mode 100644 index 00000000..1012847a --- /dev/null +++ b/vripper-web-ui/src/app/domain/settings.model.ts @@ -0,0 +1,32 @@ +export interface Settings { + desktopClipboard: boolean; + maxEventLog: number; + connectionSettings: ConnectionSettings; + downloadSettings: DownloadSettings; + viperSettings: ViperSettings; +} + +export interface ConnectionSettings { + maxThreads: number; + maxTotalThreads: number; + timeout: number; + maxAttempts: number; +} + +export interface DownloadSettings { + downloadPath: string; + autoStart: boolean; + forceOrder: boolean; + forumSubfolder: boolean; + threadSubLocation: boolean; + clearCompleted: boolean; + appendPostId: boolean; +} + +export interface ViperSettings { + login: boolean; + username: string; + password: string; + thanks: boolean; + proxy: string; +} diff --git a/vripper-ui/src/app/domain/vr-post-parse.model.ts b/vripper-web-ui/src/app/domain/vr-post-parse.model.ts similarity index 100% rename from vripper-ui/src/app/domain/vr-post-parse.model.ts rename to vripper-web-ui/src/app/domain/vr-post-parse.model.ts diff --git a/vripper-ui/src/app/domain/ws-message.model.ts b/vripper-web-ui/src/app/domain/ws-message.model.ts similarity index 100% rename from vripper-ui/src/app/domain/ws-message.model.ts rename to vripper-web-ui/src/app/domain/ws-message.model.ts diff --git a/vripper-web-ui/src/app/event-log/event-log.component.html b/vripper-web-ui/src/app/event-log/event-log.component.html new file mode 100644 index 00000000..ba1b0df4 --- /dev/null +++ b/vripper-web-ui/src/app/event-log/event-log.component.html @@ -0,0 +1,4 @@ +
+ + +
diff --git a/vripper-ui/src/app/event-log/event-log.component.ts b/vripper-web-ui/src/app/event-log/event-log.component.ts similarity index 87% rename from vripper-ui/src/app/event-log/event-log.component.ts rename to vripper-web-ui/src/app/event-log/event-log.component.ts index c4f1dc15..f8018bae 100644 --- a/vripper-ui/src/app/event-log/event-log.component.ts +++ b/vripper-web-ui/src/app/event-log/event-log.component.ts @@ -1,10 +1,11 @@ -import {ChangeDetectionStrategy, Component, NgZone, OnDestroy} from '@angular/core'; +import {ChangeDetectionStrategy, Component, NgZone, OnDestroy, ViewChild} from '@angular/core'; import {GridOptions} from 'ag-grid-community'; import {WsConnectionService} from '../services/ws-connection.service'; import {EventLogDatasource} from './event-log.datasource'; import {StatusRendererNative} from '../grid-custom-cells/status-renderer.native'; import {EventLogService} from '../services/event-log.service'; import {EventMessageRendererNative} from '../grid-custom-cells/event-message-renderer.native'; +import {AgGridAngular} from "ag-grid-angular"; @Component({ selector: 'app-event-log', @@ -13,7 +14,8 @@ import {EventMessageRendererNative} from '../grid-custom-cells/event-message-ren }) export class EventLogComponent implements OnDestroy { gridOptions: GridOptions; - dataSource: EventLogDatasource; + @ViewChild('agGrid') agGrid!: AgGridAngular; + dataSource!: EventLogDatasource; constructor( private wsConnection: WsConnectionService, @@ -77,7 +79,7 @@ export class EventLogComponent implements OnDestroy { headerHeight: 35, animateRows: true, rowSelection: 'single', - rowDeselection: true, + suppressRowDeselection: false, rowData: [], components: { nativeStatusCellRenderer: StatusRendererNative, @@ -85,14 +87,13 @@ export class EventLogComponent implements OnDestroy { }, overlayLoadingTemplate: '', overlayNoRowsTemplate: '', - getRowNodeId: data => data['id'], + getRowId: row => row.data['id'], onGridReady: () => { - this.eventLogService.setGridApi(this.gridOptions.api); - this.dataSource = new EventLogDatasource(this.wsConnection, this.gridOptions, this.zone); + this.eventLogService.setGridApi(this.agGrid.api); + this.dataSource = new EventLogDatasource(this.wsConnection, this.agGrid.api, this.zone); this.dataSource.connect(); }, - onRowDataUpdated: () => this.eventLogService.setCount(this.gridOptions.api.getDisplayedRowCount()), - onRowDataChanged: () => this.eventLogService.setCount(this.gridOptions.api.getDisplayedRowCount()), + onRowDataUpdated: () => this.eventLogService.setCount(this.agGrid.api.getDisplayedRowCount()), }; } diff --git a/vripper-ui/src/app/event-log/event-log.datasource.ts b/vripper-web-ui/src/app/event-log/event-log.datasource.ts similarity index 62% rename from vripper-ui/src/app/event-log/event-log.datasource.ts rename to vripper-web-ui/src/app/event-log/event-log.datasource.ts index 1978d077..bbc253e0 100644 --- a/vripper-ui/src/app/event-log/event-log.datasource.ts +++ b/vripper-web-ui/src/app/event-log/event-log.datasource.ts @@ -1,42 +1,42 @@ import {Subscription} from 'rxjs'; import {WsConnectionService} from '../services/ws-connection.service'; -import {GridOptions, RowNode} from 'ag-grid-community'; +import {GridApi, IRowNode} from 'ag-grid-community'; import {NgZone} from '@angular/core'; import {EventLog} from '../domain/event.model'; export class EventLogDatasource { subscriptions: Subscription[] = []; - constructor(private ws: WsConnectionService, private gridOptions: GridOptions, private zone: NgZone) { + constructor(private ws: WsConnectionService, private gridApi: GridApi, private zone: NgZone) { } connect() { this.subscriptions.push(this.ws.events$.subscribe((e: EventLog[]) => { this.zone.run(() => { - const toAdd = []; - const toUpdate = []; + const toAdd: EventLog[] = []; + const toUpdate: EventLog[] = []; e.forEach(v => { - if (this.gridOptions.api.getRowNode(v.id.toString(10)) == null) { + if (this.gridApi.getRowNode(v.id.toString(10)) == null) { toAdd.push(v); } else { toUpdate.push(v); } }); - this.gridOptions.api.applyTransaction({update: toUpdate, add: toAdd}); + this.gridApi.applyTransaction({update: toUpdate, add: toAdd}); }); })); this.subscriptions.push(this.ws.eventsRemove$.subscribe((e: number[]) => { this.zone.run(() => { - const toRemove = []; + const toRemove: any[] = []; e.forEach(v => { - const rowNode: RowNode = this.gridOptions.api.getRowNode(v.toString(10)); + const rowNode: IRowNode | undefined = this.gridApi.getRowNode(v.toString(10)); if (rowNode != null) { toRemove.push(rowNode.data); } return; }); - this.gridOptions.api.applyTransaction({remove: toRemove}); + this.gridApi.applyTransaction({remove: toRemove}); }); })); } diff --git a/vripper-ui/src/app/event-log/message-dialog/event-log-message-dialog.component.html b/vripper-web-ui/src/app/event-log/message-dialog/event-log-message-dialog.component.html similarity index 100% rename from vripper-ui/src/app/event-log/message-dialog/event-log-message-dialog.component.html rename to vripper-web-ui/src/app/event-log/message-dialog/event-log-message-dialog.component.html diff --git a/vripper-ui/src/app/event-log/message-dialog/event-log-message-dialog.component.ts b/vripper-web-ui/src/app/event-log/message-dialog/event-log-message-dialog.component.ts similarity index 100% rename from vripper-ui/src/app/event-log/message-dialog/event-log-message-dialog.component.ts rename to vripper-web-ui/src/app/event-log/message-dialog/event-log-message-dialog.component.ts diff --git a/vripper-ui/src/app/grid-custom-cells/collector-actions-renderer.native.ts b/vripper-web-ui/src/app/grid-custom-cells/collector-actions-renderer.native.ts similarity index 89% rename from vripper-ui/src/app/grid-custom-cells/collector-actions-renderer.native.ts rename to vripper-web-ui/src/app/grid-custom-cells/collector-actions-renderer.native.ts index 4f52049d..615b9370 100644 --- a/vripper-ui/src/app/grid-custom-cells/collector-actions-renderer.native.ts +++ b/vripper-web-ui/src/app/grid-custom-cells/collector-actions-renderer.native.ts @@ -3,14 +3,14 @@ import {MultiPostService} from '../services/multi-post.service'; import {MultiPostModel} from '../domain/multi-post.model'; export class CollectorActionsRendererNative implements ICellRendererComp { - private gui: HTMLElement; - private selectSpan: HTMLSpanElement; - private selectButton: HTMLButtonElement; - private deleteSpan: HTMLSpanElement; - private deleteButton: HTMLButtonElement; - private multiPostService: MultiPostService; - private multiPostModel: MultiPostModel; - private gridApi: GridApi; + private gui!: HTMLElement; + private selectSpan!: HTMLSpanElement; + private selectButton!: HTMLButtonElement; + private deleteSpan!: HTMLSpanElement; + private deleteButton!: HTMLButtonElement; + private multiPostService!: MultiPostService; + private multiPostModel!: MultiPostModel; + private gridApi!: GridApi; destroy(): void { this.selectButton.removeEventListener('click', () => this.multiPostService.selectItems(this.multiPostModel)); diff --git a/vripper-ui/src/app/grid-custom-cells/event-message-renderer.native.ts b/vripper-web-ui/src/app/grid-custom-cells/event-message-renderer.native.ts similarity index 88% rename from vripper-ui/src/app/grid-custom-cells/event-message-renderer.native.ts rename to vripper-web-ui/src/app/grid-custom-cells/event-message-renderer.native.ts index ca5c755e..e444d033 100644 --- a/vripper-ui/src/app/grid-custom-cells/event-message-renderer.native.ts +++ b/vripper-web-ui/src/app/grid-custom-cells/event-message-renderer.native.ts @@ -3,13 +3,13 @@ import {EventLog} from '../domain/event.model'; import {EventLogService} from '../services/event-log.service'; export class EventMessageRendererNative implements ICellRendererComp { - private gui: HTMLElement; - private viewSpan: HTMLSpanElement; - private viewButton: HTMLButtonElement; - private eventLogService: EventLogService; - private eventLog: EventLog; - private gridApi: GridApi; - private text: HTMLSpanElement; + private gui!: HTMLElement; + private viewSpan!: HTMLSpanElement; + private viewButton!: HTMLButtonElement; + private eventLogService!: EventLogService; + private eventLog!: EventLog; + private gridApi!: GridApi; + private text!: HTMLSpanElement; destroy(): void { this.viewButton.removeEventListener('click', () => this.eventLogService.openDialog(this.eventLog.message)); diff --git a/vripper-ui/src/app/grid-custom-cells/post-added-renderer.native.ts b/vripper-web-ui/src/app/grid-custom-cells/post-added-renderer.native.ts similarity index 81% rename from vripper-ui/src/app/grid-custom-cells/post-added-renderer.native.ts rename to vripper-web-ui/src/app/grid-custom-cells/post-added-renderer.native.ts index 768cb6ce..dbf30b68 100644 --- a/vripper-ui/src/app/grid-custom-cells/post-added-renderer.native.ts +++ b/vripper-web-ui/src/app/grid-custom-cells/post-added-renderer.native.ts @@ -1,13 +1,13 @@ -import {GridApi, ICellRendererComp, ICellRendererParams, RowNode} from 'ag-grid-community'; +import {GridApi, ICellRendererComp, ICellRendererParams, IRowNode} from 'ag-grid-community'; import {PostContextMenuService} from '../services/post-context-menu.service'; export class PostAddedRendererNative implements ICellRendererComp { - private gui: HTMLElement; - private addedOn: string; - private text: HTMLSpanElement; - private gridApi: GridApi; - private node: RowNode; - private contextMenuService: PostContextMenuService; + private gui!: HTMLElement; + private addedOn!: string; + private text!: HTMLSpanElement; + private gridApi!: GridApi; + private node!: IRowNode; + private contextMenuService!: PostContextMenuService; destroy(): void { if (this.gui) { diff --git a/vripper-ui/src/app/grid-custom-cells/post-alt-renderer.native.ts b/vripper-web-ui/src/app/grid-custom-cells/post-alt-renderer.native.ts similarity index 83% rename from vripper-ui/src/app/grid-custom-cells/post-alt-renderer.native.ts rename to vripper-web-ui/src/app/grid-custom-cells/post-alt-renderer.native.ts index 74de5b5b..72a24a2e 100644 --- a/vripper-ui/src/app/grid-custom-cells/post-alt-renderer.native.ts +++ b/vripper-web-ui/src/app/grid-custom-cells/post-alt-renderer.native.ts @@ -1,13 +1,13 @@ -import {GridApi, ICellRendererComp, ICellRendererParams, RowNode} from 'ag-grid-community'; +import {GridApi, ICellRendererComp, ICellRendererParams, IRowNode} from 'ag-grid-community'; import {PostContextMenuService} from '../services/post-context-menu.service'; export class PostAltRendererNative implements ICellRendererComp { - private gui: HTMLElement; - private names: string[]; - private text: HTMLSpanElement; - private gridApi: GridApi; - private node: RowNode; - private contextMenuService: PostContextMenuService; + private gui!: HTMLElement; + private names!: string[]; + private text!: HTMLSpanElement; + private gridApi!: GridApi; + private node!: IRowNode; + private contextMenuService!: PostContextMenuService; destroy(): void { if (this.gui) { diff --git a/vripper-ui/src/app/grid-custom-cells/post-files-renderer.native.ts b/vripper-web-ui/src/app/grid-custom-cells/post-files-renderer.native.ts similarity index 83% rename from vripper-ui/src/app/grid-custom-cells/post-files-renderer.native.ts rename to vripper-web-ui/src/app/grid-custom-cells/post-files-renderer.native.ts index 62694b38..742f5359 100644 --- a/vripper-ui/src/app/grid-custom-cells/post-files-renderer.native.ts +++ b/vripper-web-ui/src/app/grid-custom-cells/post-files-renderer.native.ts @@ -1,16 +1,16 @@ -import {GridApi, ICellRendererComp, ICellRendererParams, RowNode} from 'ag-grid-community'; +import {GridApi, ICellRendererComp, ICellRendererParams, IRowNode} from 'ag-grid-community'; import {PostContextMenuService} from '../services/post-context-menu.service'; export class PostFilesRendererNative implements ICellRendererComp { - private gui: HTMLElement; - private done: number; - private total: number; - private hosts: string[]; - private text: HTMLElement; - private gridApi: GridApi; - private node: RowNode; - private contextMenuService: PostContextMenuService; + private gui!: HTMLElement; + private done!: number; + private total!: number; + private hosts!: string[]; + private text!: HTMLElement; + private gridApi!: GridApi; + private node!: IRowNode; + private contextMenuService!: PostContextMenuService; destroy(): void { if (this.gui) { diff --git a/vripper-ui/src/app/grid-custom-cells/post-order-renderer.native.ts b/vripper-web-ui/src/app/grid-custom-cells/post-order-renderer.native.ts similarity index 82% rename from vripper-ui/src/app/grid-custom-cells/post-order-renderer.native.ts rename to vripper-web-ui/src/app/grid-custom-cells/post-order-renderer.native.ts index 7be5e5fc..57b540e4 100644 --- a/vripper-ui/src/app/grid-custom-cells/post-order-renderer.native.ts +++ b/vripper-web-ui/src/app/grid-custom-cells/post-order-renderer.native.ts @@ -1,13 +1,13 @@ -import {GridApi, ICellRendererComp, ICellRendererParams, RowNode} from 'ag-grid-community'; +import {GridApi, ICellRendererComp, ICellRendererParams, IRowNode} from 'ag-grid-community'; import {PostContextMenuService} from '../services/post-context-menu.service'; export class PostOrderRendererNative implements ICellRendererComp { - private gui: HTMLElement; - private order: number; - private text: HTMLSpanElement; - private gridApi: GridApi; - private node: RowNode; - private contextMenuService: PostContextMenuService; + private gui!: HTMLElement; + private order!: number; + private text!: HTMLSpanElement; + private gridApi!: GridApi; + private node!: IRowNode; + private contextMenuService!: PostContextMenuService; destroy(): void { if (this.gui) { diff --git a/vripper-ui/src/app/grid-custom-cells/post-progress-renderer.native.ts b/vripper-web-ui/src/app/grid-custom-cells/post-progress-renderer.native.ts similarity index 75% rename from vripper-ui/src/app/grid-custom-cells/post-progress-renderer.native.ts rename to vripper-web-ui/src/app/grid-custom-cells/post-progress-renderer.native.ts index a14dc580..72e3c82e 100644 --- a/vripper-ui/src/app/grid-custom-cells/post-progress-renderer.native.ts +++ b/vripper-web-ui/src/app/grid-custom-cells/post-progress-renderer.native.ts @@ -1,20 +1,20 @@ -import {GridApi, ICellRendererParams, RowNode} from 'ag-grid-community'; +import {GridApi, ICellRendererParams, IRowNode} from 'ag-grid-community'; import {PostContextMenuService} from '../services/post-context-menu.service'; import {ProgressRendererNative} from './progress-renderer.native'; export class PostProgressRendererNative extends ProgressRendererNative { - private gridApi: GridApi; - private node: RowNode; - private contextMenuService: PostContextMenuService; + private gridApi!: GridApi; + private node!: IRowNode; + private contextMenuService!: PostContextMenuService; - destroy(): void { + override destroy(): void { if (this.gui) { this.gui.removeEventListener('contextmenu', this.context.bind(this)); } } - init(params: ICellRendererParams): void { + override init(params: ICellRendererParams): void { super.init(params); // @ts-ignore this.contextMenuService = params.contextMenuService; diff --git a/vripper-ui/src/app/grid-custom-cells/post-status-renderer.native.ts b/vripper-web-ui/src/app/grid-custom-cells/post-status-renderer.native.ts similarity index 74% rename from vripper-ui/src/app/grid-custom-cells/post-status-renderer.native.ts rename to vripper-web-ui/src/app/grid-custom-cells/post-status-renderer.native.ts index 816d2dfe..508ba8ca 100644 --- a/vripper-ui/src/app/grid-custom-cells/post-status-renderer.native.ts +++ b/vripper-web-ui/src/app/grid-custom-cells/post-status-renderer.native.ts @@ -1,20 +1,20 @@ import {StatusRendererNative} from './status-renderer.native'; -import {GridApi, ICellRendererParams, RowNode} from 'ag-grid-community'; +import {GridApi, ICellRendererParams, IRowNode} from 'ag-grid-community'; import {PostContextMenuService} from '../services/post-context-menu.service'; export class PostStatusRendererNative extends StatusRendererNative { - private gridApi: GridApi; - private node: RowNode; - private contextMenuService: PostContextMenuService; + private gridApi!: GridApi; + private node!: IRowNode; + private contextMenuService!: PostContextMenuService; - destroy(): void { + override destroy(): void { if (this.gui) { this.gui.removeEventListener('contextmenu', this.context.bind(this)); } } - init(params: ICellRendererParams): void { + override init(params: ICellRendererParams): void { super.init(params); // @ts-ignore this.contextMenuService = params.contextMenuService; diff --git a/vripper-ui/src/app/grid-custom-cells/progress-renderer.native.ts b/vripper-web-ui/src/app/grid-custom-cells/progress-renderer.native.ts similarity index 87% rename from vripper-ui/src/app/grid-custom-cells/progress-renderer.native.ts rename to vripper-web-ui/src/app/grid-custom-cells/progress-renderer.native.ts index 0be9cb35..e6a6688e 100644 --- a/vripper-ui/src/app/grid-custom-cells/progress-renderer.native.ts +++ b/vripper-web-ui/src/app/grid-custom-cells/progress-renderer.native.ts @@ -2,10 +2,10 @@ import {ICellRendererComp, ICellRendererParams} from 'ag-grid-community'; export class ProgressRendererNative implements ICellRendererComp { - protected gui: HTMLElement; - protected progressBackDiv: HTMLElement; - protected progressFrontDiv: HTMLElement; - private progress: number; + protected gui!: HTMLElement; + protected progressBackDiv!: HTMLElement; + protected progressFrontDiv!: HTMLElement; + private progress!: number; destroy(): void { } diff --git a/vripper-ui/src/app/grid-custom-cells/status-renderer.native.ts b/vripper-web-ui/src/app/grid-custom-cells/status-renderer.native.ts similarity index 91% rename from vripper-ui/src/app/grid-custom-cells/status-renderer.native.ts rename to vripper-web-ui/src/app/grid-custom-cells/status-renderer.native.ts index 6a929c49..a6ae032f 100644 --- a/vripper-ui/src/app/grid-custom-cells/status-renderer.native.ts +++ b/vripper-web-ui/src/app/grid-custom-cells/status-renderer.native.ts @@ -2,12 +2,12 @@ import {ICellRendererComp, ICellRendererParams} from 'ag-grid-community'; export class StatusRendererNative implements ICellRendererComp { - protected gui: HTMLElement; - private container: HTMLElement; - private icon: HTMLElement; - private text: HTMLElement; - private status: string; - private currentStatusClass; + protected gui!: HTMLElement; + private container!: HTMLElement; + private icon!: HTMLElement; + private text!: HTMLElement; + private status!: string; + private currentStatusClass: string | undefined; destroy(): void { } @@ -26,8 +26,8 @@ export class StatusRendererNative implements ICellRendererComp { this.icon = document.createElement('span'); this.currentStatusClass = this.getClass(this.status); - this.icon.classList.add('native-status-icon', this.currentStatusClass); - this.icon.innerHTML = this.getIcon(this.status); + this.icon.classList.add('native-status-icon', this.currentStatusClass ?? ''); + this.icon.innerHTML = this.getIcon(this.status) ?? ''; this.text = document.createElement('span'); this.text.classList.add('native-status-text'); @@ -41,9 +41,8 @@ export class StatusRendererNative implements ICellRendererComp { const oldStatusClass = this.currentStatusClass; this.status = params.value; this.currentStatusClass = this.getClass(this.status); - this.icon.classList.replace(oldStatusClass, this.currentStatusClass); + this.icon.classList.replace(oldStatusClass ?? '', this.currentStatusClass ?? ''); this.icon.innerHTML = this.getIcon(this.status); - this.text.innerText = this.titleCase(this.status); return true; } @@ -83,6 +82,8 @@ export class StatusRendererNative implements ICellRendererComp { return ` `; + default: + return ''; } } @@ -93,7 +94,7 @@ export class StatusRendererNative implements ICellRendererComp { case 'DOWNLOADING': case 'PROCESSING': return 'downloading'; - case 'COMPLETE': + case 'FINISHED': case 'DONE': return 'complete'; case 'ERROR': @@ -102,6 +103,8 @@ export class StatusRendererNative implements ICellRendererComp { return 'stopped'; case 'PARTIAL': return 'downloading'; + default: + return '' } } } diff --git a/vripper-ui/src/app/grid-custom-cells/title-renderer.native.ts b/vripper-web-ui/src/app/grid-custom-cells/title-renderer.native.ts similarity index 88% rename from vripper-ui/src/app/grid-custom-cells/title-renderer.native.ts rename to vripper-web-ui/src/app/grid-custom-cells/title-renderer.native.ts index 95b380aa..6b04dbe4 100644 --- a/vripper-ui/src/app/grid-custom-cells/title-renderer.native.ts +++ b/vripper-web-ui/src/app/grid-custom-cells/title-renderer.native.ts @@ -1,4 +1,4 @@ -import {GridApi, ICellRendererComp, ICellRendererParams, RowNode} from 'ag-grid-community'; +import {GridApi, ICellRendererComp, ICellRendererParams, IRowNode} from 'ag-grid-community'; import {PostContextMenuService} from '../services/post-context-menu.service'; import {Overlay, OverlayPositionBuilder, OverlayRef} from '@angular/cdk/overlay'; import {ComponentPortal} from '@angular/cdk/portal'; @@ -7,17 +7,17 @@ import {ComponentRef, NgZone} from '@angular/core'; export class TitleRendererNative implements ICellRendererComp { tooltipPortal = new ComponentPortal(AppPreviewComponent); - appPreview: string[]; - private gui: HTMLElement; - private text: HTMLSpanElement; - private gridApi: GridApi; - private node: RowNode; - private contextMenuService: PostContextMenuService; - private icon: HTMLSpanElement; - private overlayPositionBuilder: OverlayPositionBuilder; - private overlay: Overlay; - private zone: NgZone; - private overlayRef: OverlayRef; + appPreview!: string[]; + private gui!: HTMLElement; + private text!: HTMLSpanElement; + private gridApi!: GridApi; + private node!: IRowNode; + private contextMenuService!: PostContextMenuService; + private icon!: HTMLSpanElement; + private overlayPositionBuilder!: OverlayPositionBuilder; + private overlay!: Overlay; + private zone!: NgZone; + private overlayRef!: OverlayRef; destroy(): void { if (this.gui) { diff --git a/vripper-ui/src/app/grid-custom-cells/url-renderer.native.ts b/vripper-web-ui/src/app/grid-custom-cells/url-renderer.native.ts similarity index 67% rename from vripper-ui/src/app/grid-custom-cells/url-renderer.native.ts rename to vripper-web-ui/src/app/grid-custom-cells/url-renderer.native.ts index ac4c6f23..bf19e6f3 100644 --- a/vripper-ui/src/app/grid-custom-cells/url-renderer.native.ts +++ b/vripper-web-ui/src/app/grid-custom-cells/url-renderer.native.ts @@ -1,13 +1,11 @@ import {ICellRendererComp, ICellRendererParams} from 'ag-grid-community'; -import {ElectronService} from 'ngx-electron'; export class UrlRendererNative implements ICellRendererComp { - private electronService: ElectronService; - private gui: HTMLElement; - private text: HTMLSpanElement; - private link: HTMLAnchorElement; - private url: string; + private gui!: HTMLElement; + private text!: HTMLSpanElement; + private link!: HTMLAnchorElement; + private url!: string; destroy(): void { if (this.link) { @@ -20,9 +18,6 @@ export class UrlRendererNative implements ICellRendererComp { } init(params: ICellRendererParams): void { - // @ts-ignore - this.electronService = params.electronService; - this.url = params.value; this.gui = document.createElement('div'); this.gui.classList.add('text-cell'); @@ -43,10 +38,6 @@ export class UrlRendererNative implements ICellRendererComp { } goTo() { - if (this.electronService.isElectronApp) { - this.electronService.shell.openExternal(this.url).then(); - } else { - window.open(this.url, '_blank'); - } + window.open(this.url, '_blank'); } } diff --git a/vripper-ui/src/app/home/home.component.html b/vripper-web-ui/src/app/home/home.component.html similarity index 100% rename from vripper-ui/src/app/home/home.component.html rename to vripper-web-ui/src/app/home/home.component.html diff --git a/vripper-ui/src/app/home/home.component.scss b/vripper-web-ui/src/app/home/home.component.scss similarity index 89% rename from vripper-ui/src/app/home/home.component.scss rename to vripper-web-ui/src/app/home/home.component.scss index c9244f6a..be806752 100644 --- a/vripper-ui/src/app/home/home.component.scss +++ b/vripper-web-ui/src/app/home/home.component.scss @@ -2,10 +2,6 @@ table { width: 100%; } -:host ::ng-deep .mat-tab-body-wrapper { - height: 100%; -} - .app-custom-plain-container-with-desc { align-items: center; display: flex; @@ -44,7 +40,7 @@ table { height: auto; margin-right: 2px; width: 200px; - align-self: start; + align-self: flex-start; } } diff --git a/vripper-ui/src/app/home/home.component.ts b/vripper-web-ui/src/app/home/home.component.ts similarity index 57% rename from vripper-ui/src/app/home/home.component.ts rename to vripper-web-ui/src/app/home/home.component.ts index b5f78d87..168d7072 100644 --- a/vripper-ui/src/app/home/home.component.ts +++ b/vripper-web-ui/src/app/home/home.component.ts @@ -1,7 +1,6 @@ import {Subscription} from 'rxjs'; import {LinkCollectorService} from '../services/link-collector.service'; import {ServerService} from '../services/server-service'; -import {ClipboardService} from '../services/clipboard.service'; import {ChangeDetectionStrategy, Component, NgZone, OnDestroy, OnInit} from '@angular/core'; import {MatDialog} from '@angular/material/dialog'; import {MatSnackBar} from '@angular/material/snack-bar'; @@ -17,11 +16,9 @@ import {HomeTabsService} from '../services/home-tabs.service'; styleUrls: ['./home.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush }) -export class HomeComponent implements OnInit, OnDestroy { - clipboardSub: Subscription; +export class HomeComponent { constructor( - private clipboardService: ClipboardService, public dialog: MatDialog, private httpClient: HttpClient, private serverService: ServerService, @@ -34,33 +31,6 @@ export class HomeComponent implements OnInit, OnDestroy { ) { } - ngOnInit() { - this.clipboardSub = this.clipboardService.links.subscribe(e => { - this.ngZone.run(() => { - this.httpClient - .post<{ threadId: string; postId: string }>(this.serverService.baseUrl + '/post', {url: e}) - .subscribe( - response => { - this._snackBar.open('Clipboard scan complete', null, { - duration: 5000 - }); - }, - error => { - this._snackBar.open(error?.error?.message || 'Unexpected error, check log file', null, { - duration: 5000 - }); - } - ); - }); - }); - } - - ngOnDestroy() { - if (this.clipboardSub != null) { - this.clipboardSub.unsubscribe(); - } - } - onTabChange($event: MatTabChangeEvent) { this.homeTabsService.setIndex($event.index); } diff --git a/vripper-ui/src/app/material.module.ts b/vripper-web-ui/src/app/material.module.ts similarity index 100% rename from vripper-ui/src/app/material.module.ts rename to vripper-web-ui/src/app/material.module.ts diff --git a/vripper-ui/src/app/multi-post-grid/multi-post-grid-data.source.ts b/vripper-web-ui/src/app/multi-post-grid/multi-post-grid-data.source.ts similarity index 63% rename from vripper-ui/src/app/multi-post-grid/multi-post-grid-data.source.ts rename to vripper-web-ui/src/app/multi-post-grid/multi-post-grid-data.source.ts index 3c47cecb..78971114 100644 --- a/vripper-ui/src/app/multi-post-grid/multi-post-grid-data.source.ts +++ b/vripper-web-ui/src/app/multi-post-grid/multi-post-grid-data.source.ts @@ -1,7 +1,7 @@ import {MultiPostModel} from '../domain/multi-post.model'; import {Subscription} from 'rxjs'; import {WsConnectionService} from '../services/ws-connection.service'; -import {GridOptions, RowNode} from 'ag-grid-community'; +import {GridApi, IRowNode, RowNode} from 'ag-grid-community'; import {NgZone} from '@angular/core'; export class MultiPostGridDataSource { @@ -9,7 +9,7 @@ export class MultiPostGridDataSource { constructor( private ws: WsConnectionService, - private gridOptions: GridOptions, + private gridApi: GridApi, private zone: NgZone ) { } @@ -17,31 +17,31 @@ export class MultiPostGridDataSource { connect() { this.subscriptions.push(this.ws.multiPosts$.subscribe((e: MultiPostModel[]) => { this.zone.run(() => { - const toAdd = []; - const toUpdate = []; + const toAdd: MultiPostModel[] = []; + const toUpdate: MultiPostModel[] = []; e.forEach(v => { - const rowNode: RowNode = this.gridOptions.api.getRowNode(v.threadId); + const rowNode: IRowNode | undefined = this.gridApi.getRowNode(v.threadId); if (rowNode == null) { toAdd.push(v); } else { toUpdate.push(v); } }); - this.gridOptions.api.applyTransaction({update: toUpdate, add: toAdd}); + this.gridApi.applyTransaction({update: toUpdate, add: toAdd}); }); })); this.subscriptions.push(this.ws.queuedRemove$.subscribe((e: string[]) => { this.zone.run(() => { - const toRemove = []; + const toRemove: MultiPostModel[] = []; e.forEach(v => { - const rowNode: RowNode = this.gridOptions.api.getRowNode(v); - if (rowNode != null) { + const rowNode: IRowNode | undefined = this.gridApi.getRowNode(v); + if (rowNode != null && rowNode.data != null) { toRemove.push(rowNode.data); } return; }); - this.gridOptions.api.applyTransaction({remove: toRemove}); + this.gridApi.applyTransaction({remove: toRemove}); }); })); } diff --git a/vripper-web-ui/src/app/multi-post-grid/multi-post-grid.component.html b/vripper-web-ui/src/app/multi-post-grid/multi-post-grid.component.html new file mode 100644 index 00000000..ba1b0df4 --- /dev/null +++ b/vripper-web-ui/src/app/multi-post-grid/multi-post-grid.component.html @@ -0,0 +1,4 @@ +
+ + +
diff --git a/vripper-ui/src/app/multi-post-grid/multi-post-grid.component.ts b/vripper-web-ui/src/app/multi-post-grid/multi-post-grid.component.ts similarity index 75% rename from vripper-ui/src/app/multi-post-grid/multi-post-grid.component.ts rename to vripper-web-ui/src/app/multi-post-grid/multi-post-grid.component.ts index f7c1fee2..2f27a467 100644 --- a/vripper-ui/src/app/multi-post-grid/multi-post-grid.component.ts +++ b/vripper-web-ui/src/app/multi-post-grid/multi-post-grid.component.ts @@ -1,12 +1,12 @@ import {LinkCollectorService} from '../services/link-collector.service'; import {MultiPostGridDataSource} from './multi-post-grid-data.source'; -import {ChangeDetectionStrategy, Component, NgZone, OnDestroy} from '@angular/core'; +import {ChangeDetectionStrategy, Component, NgZone, OnDestroy, ViewChild} from '@angular/core'; import {GridOptions} from 'ag-grid-community'; import {WsConnectionService} from '../services/ws-connection.service'; import {CollectorActionsRendererNative} from '../grid-custom-cells/collector-actions-renderer.native'; import {MultiPostService} from '../services/multi-post.service'; import {UrlRendererNative} from '../grid-custom-cells/url-renderer.native'; -import {ElectronService} from 'ngx-electron'; +import {AgGridAngular} from "ag-grid-angular"; @Component({ selector: 'app-grab-queue', @@ -16,14 +16,15 @@ import {ElectronService} from 'ngx-electron'; export class MultiPostGridComponent implements OnDestroy { gridOptions: GridOptions; - dataSource: MultiPostGridDataSource; + + @ViewChild('agGrid') agGrid!: AgGridAngular; + dataSource!: MultiPostGridDataSource; constructor( private wsConnection: WsConnectionService, private zone: NgZone, private linkCollectorService: LinkCollectorService, - private grabQueueService: MultiPostService, - private electronService: ElectronService) { + private grabQueueService: MultiPostService) { this.gridOptions = { columnDefs: [ { @@ -31,9 +32,6 @@ export class MultiPostGridComponent implements OnDestroy { field: 'link', sort: 'asc', cellRenderer: 'urlCellRenderer', - cellRendererParams: { - electronService: this.electronService - }, flex: 1 }, { headerName: 'Count', @@ -65,14 +63,13 @@ export class MultiPostGridComponent implements OnDestroy { }, overlayLoadingTemplate: '', overlayNoRowsTemplate: '', - getRowNodeId: data => data['threadId'], - onGridReady: () => { - this.linkCollectorService.setGridApi(this.gridOptions.api); - this.dataSource = new MultiPostGridDataSource(this.wsConnection, this.gridOptions, this.zone); + getRowId: row => row.data['threadId'], + onGridReady: (prams) => { + this.linkCollectorService.setGridApi(this.agGrid.api); + this.dataSource = new MultiPostGridDataSource(this.wsConnection, this.agGrid.api, this.zone); this.dataSource.connect(); }, - onRowDataUpdated: () => this.linkCollectorService.setCount(this.gridOptions.api.getDisplayedRowCount()), - onRowDataChanged: () => this.linkCollectorService.setCount(this.gridOptions.api.getDisplayedRowCount()), + onRowDataUpdated: () => this.linkCollectorService.setCount(this.agGrid.api.getDisplayedRowCount()), }; } diff --git a/vripper-ui/src/app/multi-post-items/multi-post-items.component.html b/vripper-web-ui/src/app/multi-post-items/multi-post-items.component.html similarity index 100% rename from vripper-ui/src/app/multi-post-items/multi-post-items.component.html rename to vripper-web-ui/src/app/multi-post-items/multi-post-items.component.html diff --git a/vripper-ui/src/app/multi-post-items/multi-post-items.component.scss b/vripper-web-ui/src/app/multi-post-items/multi-post-items.component.scss similarity index 100% rename from vripper-ui/src/app/multi-post-items/multi-post-items.component.scss rename to vripper-web-ui/src/app/multi-post-items/multi-post-items.component.scss diff --git a/vripper-ui/src/app/multi-post-items/multi-post-items.component.ts b/vripper-web-ui/src/app/multi-post-items/multi-post-items.component.ts similarity index 79% rename from vripper-ui/src/app/multi-post-items/multi-post-items.component.ts rename to vripper-web-ui/src/app/multi-post-items/multi-post-items.component.ts index 47d0efcd..d2eb1e17 100644 --- a/vripper-ui/src/app/multi-post-items/multi-post-items.component.ts +++ b/vripper-web-ui/src/app/multi-post-items/multi-post-items.component.ts @@ -3,7 +3,7 @@ import {MultiPostItem} from '../domain/vr-post-parse.model'; import {ChangeDetectionStrategy, Component, Inject, NgZone} from '@angular/core'; import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog'; import {MatSnackBar} from '@angular/material/snack-bar'; -import {GridOptions, RowNode} from 'ag-grid-community'; +import {GridApi, GridOptions, IRowNode} from 'ag-grid-community'; import {BehaviorSubject, Subject} from 'rxjs'; import {HttpClient} from '@angular/common/http'; import {ServerService} from '../services/server-service'; @@ -13,7 +13,6 @@ import {PostContextMenuService} from '../services/post-context-menu.service'; import {Overlay, OverlayPositionBuilder} from '@angular/cdk/overlay'; import {UrlRendererNative} from '../grid-custom-cells/url-renderer.native'; import {ValueFormatterParams} from 'ag-grid-community/dist/lib/entities/colDef'; -import {ElectronService} from 'ngx-electron'; @Component({ selector: 'app-multi-post-items', @@ -23,6 +22,7 @@ import {ElectronService} from 'ngx-electron'; }) export class MultiPostItemsComponent { gridOptions: GridOptions; + gridApi!: GridApi; loading: Subject = new BehaviorSubject(true); selectedRowsCount: Subject = new BehaviorSubject(0); @@ -36,8 +36,7 @@ export class MultiPostItemsComponent { private contextMenuService: PostContextMenuService, private overlayPositionBuilder: OverlayPositionBuilder, private overlay: Overlay, - private zone: NgZone, - private electronService: ElectronService + private zone: NgZone ) { this.gridOptions = { columnDefs: [ @@ -67,9 +66,6 @@ export class MultiPostItemsComponent { headerName: 'URL', field: 'url', cellRenderer: 'urlCellRenderer', - cellRendererParams: { - electronService: this.electronService - }, flex: 1 }, { @@ -95,22 +91,25 @@ export class MultiPostItemsComponent { rowData: [], overlayLoadingTemplate: '', overlayNoRowsTemplate: '', - getRowNodeId: data => data['url'], - onGridReady: () => this.fetchMultiPostItems(), - onSelectionChanged: () => this.onSelectionChange(this.gridOptions.api.getSelectedNodes()) + getRowId: row => row.data['url'], + onGridReady: (params) => { + this.gridApi = params.api; + this.fetchMultiPostItems(); + }, + onSelectionChanged: () => this.onSelectionChange(this.gridApi.getSelectedNodes()) }; } - onSelectionChange(data: RowNode[]) { + onSelectionChange(data: IRowNode[]) { this.selectedRowsCount.next(data.length); } - search(event) { - this.gridOptions.api.setQuickFilter(event); + search(event: string) { + this.gridApi.setQuickFilter(event); } submit() { - const data = (this.gridOptions.api.getSelectedRows()).map(e => ({ + const data = (this.gridApi.getSelectedRows()).map(e => ({ postId: e.postId, threadId: this.dialogData.threadId })); @@ -124,12 +123,12 @@ export class MultiPostItemsComponent { }) ) .subscribe( - () => { - }, - error => { - this._snackBar.open(error?.error?.message || 'Unexpected error, check log file', null, { - duration: 5000 - }); + { + error: (error) => { + this._snackBar.open(error?.error?.message || 'Unexpected error, check log file', undefined, { + duration: 5000 + }); + } } ); } @@ -139,13 +138,14 @@ export class MultiPostItemsComponent { .get(this.serverService.baseUrl + '/grab/' + this.dialogData.threadId) .pipe(finalize(() => this.loading.next(false))) .subscribe( - data => { - this.gridOptions.api.applyTransaction({add: data}); - }, - error => { - this._snackBar.open(error?.error?.message || 'Unexpected error, check log file', null, { - duration: 5000 - }); + { + next: (data) => { + this.gridApi.applyTransaction({add: data}); + }, error: (error) => { + this._snackBar.open(error?.error?.message || 'Unexpected error, check log file', undefined, { + duration: 5000 + }); + } } ); } diff --git a/vripper-ui/src/app/photos/photos.component.html b/vripper-web-ui/src/app/photos/photos.component.html similarity index 86% rename from vripper-ui/src/app/photos/photos.component.html rename to vripper-web-ui/src/app/photos/photos.component.html index 6a7c7517..5034ce18 100644 --- a/vripper-ui/src/app/photos/photos.component.html +++ b/vripper-web-ui/src/app/photos/photos.component.html @@ -1,6 +1,6 @@
-

{{ dialogData.title }}

+

{{ dialogData.postTitle }}

diff --git a/vripper-ui/src/app/photos/photos.component.scss b/vripper-web-ui/src/app/photos/photos.component.scss similarity index 100% rename from vripper-ui/src/app/photos/photos.component.scss rename to vripper-web-ui/src/app/photos/photos.component.scss diff --git a/vripper-ui/src/app/photos/photos.component.ts b/vripper-web-ui/src/app/photos/photos.component.ts similarity index 85% rename from vripper-ui/src/app/photos/photos.component.ts rename to vripper-web-ui/src/app/photos/photos.component.ts index 61d2e2f5..f97a8aa6 100644 --- a/vripper-ui/src/app/photos/photos.component.ts +++ b/vripper-web-ui/src/app/photos/photos.component.ts @@ -3,11 +3,10 @@ import {ChangeDetectionStrategy, Component, Inject, NgZone, OnDestroy, OnInit} f import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog'; import {PhotosDatasource} from './photos.datasource'; import {Post} from '../domain/post-state.model'; -import {GridOptions} from 'ag-grid-community'; +import {GridApi, GridOptions} from 'ag-grid-community'; import {ProgressRendererNative} from '../grid-custom-cells/progress-renderer.native'; import {StatusRendererNative} from '../grid-custom-cells/status-renderer.native'; import {UrlRendererNative} from '../grid-custom-cells/url-renderer.native'; -import {ElectronService} from 'ngx-electron'; @Component({ selector: 'app-post-detail', @@ -18,14 +17,14 @@ import {ElectronService} from 'ngx-electron'; export class PhotosComponent implements OnInit, OnDestroy { gridOptions: GridOptions; - dataSource: PhotosDatasource; + dataSource!: PhotosDatasource; + private gridApi!: GridApi; constructor( public dialogRef: MatDialogRef, @Inject(MAT_DIALOG_DATA) public dialogData: Post, private wsConnection: WsConnectionService, - private zone: NgZone, - private electronService: ElectronService + private zone: NgZone ) { this.gridOptions = { @@ -40,9 +39,6 @@ export class PhotosComponent implements OnInit, OnDestroy { headerName: 'URL', field: 'url', cellRenderer: 'urlCellRenderer', - cellRendererParams: { - electronService: this.electronService - }, flex: 1 }, { headerName: 'Progress', @@ -73,13 +69,13 @@ export class PhotosComponent implements OnInit, OnDestroy { }, overlayLoadingTemplate: '', overlayNoRowsTemplate: '', - getRowNodeId: (data) => data['url'], - onGridReady: () => { - this.dataSource = new PhotosDatasource(this.wsConnection, this.gridOptions, this.dialogData.postId, this.zone); + getRowId: (row) => row.data['url'], + onGridReady: (params) => { + this.gridApi = params.api; + this.dataSource = new PhotosDatasource(this.wsConnection, this.gridApi, this.dialogData.postId, this.zone); this.dataSource.connect(); }, }; - } ngOnInit() { @@ -92,5 +88,4 @@ export class PhotosComponent implements OnInit, OnDestroy { ngOnDestroy(): void { this.dataSource.disconnect(); } - } diff --git a/vripper-ui/src/app/photos/photos.datasource.ts b/vripper-web-ui/src/app/photos/photos.datasource.ts similarity index 68% rename from vripper-ui/src/app/photos/photos.datasource.ts rename to vripper-web-ui/src/app/photos/photos.datasource.ts index 2032ccaf..886f4d51 100644 --- a/vripper-ui/src/app/photos/photos.datasource.ts +++ b/vripper-web-ui/src/app/photos/photos.datasource.ts @@ -1,15 +1,16 @@ -import {GridOptions} from 'ag-grid-community'; +import {GridApi} from 'ag-grid-community'; import {Subscription} from 'rxjs'; import {WsConnectionService} from '../services/ws-connection.service'; import {NgZone} from '@angular/core'; +import {Photo} from '../domain/photo-model'; export class PhotosDatasource { subscriptions: Subscription[] = []; - postDetailsSub: Subscription; + postDetailsSub!: Subscription; constructor( private ws: WsConnectionService, - private gridOptions: GridOptions, + private gridApi: GridApi, private postId: string, private zone: NgZone ) { @@ -18,16 +19,16 @@ export class PhotosDatasource { connect() { this.postDetailsSub = this.ws.postDetails$(this.postId).subscribe(e => { this.zone.run(() => { - const toAdd = []; - const toUpdate = []; + const toAdd: Photo[] = []; + const toUpdate: Photo[] = []; e.forEach(v => { - if (this.gridOptions.api.getRowNode(v.url) == null) { + if (this.gridApi.getRowNode(v.url) == null) { toAdd.push(v); } else { toUpdate.push(v); } }); - this.gridOptions.api.applyTransaction({update: toUpdate, add: toAdd}); + this.gridApi.applyTransaction({update: toUpdate, add: toAdd}); }); }); } diff --git a/vripper-ui/src/app/posts/context-menu/post-context-menu.component.html b/vripper-web-ui/src/app/posts/context-menu/post-context-menu.component.html similarity index 70% rename from vripper-ui/src/app/posts/context-menu/post-context-menu.component.html rename to vripper-web-ui/src/app/posts/context-menu/post-context-menu.component.html index 61ff852e..210cb20d 100644 --- a/vripper-ui/src/app/posts/context-menu/post-context-menu.component.html +++ b/vripper-web-ui/src/app/posts/context-menu/post-context-menu.component.html @@ -28,21 +28,9 @@ list Photos - - - diff --git a/vripper-ui/src/app/posts/context-menu/post-context-menu.component.scss b/vripper-web-ui/src/app/posts/context-menu/post-context-menu.component.scss similarity index 100% rename from vripper-ui/src/app/posts/context-menu/post-context-menu.component.scss rename to vripper-web-ui/src/app/posts/context-menu/post-context-menu.component.scss diff --git a/vripper-ui/src/app/posts/context-menu/post-context-menu.component.ts b/vripper-web-ui/src/app/posts/context-menu/post-context-menu.component.ts similarity index 52% rename from vripper-ui/src/app/posts/context-menu/post-context-menu.component.ts rename to vripper-web-ui/src/app/posts/context-menu/post-context-menu.component.ts index ba84bb2d..8c94b6b8 100644 --- a/vripper-ui/src/app/posts/context-menu/post-context-menu.component.ts +++ b/vripper-web-ui/src/app/posts/context-menu/post-context-menu.component.ts @@ -1,22 +1,19 @@ import {AppService} from '../../services/app.service'; import {ServerService} from '../../services/server-service'; -import {HttpClient, HttpErrorResponse} from '@angular/common/http'; -import {ElectronService} from 'ngx-electron'; +import {HttpClient} from '@angular/common/http'; import {ChangeDetectionStrategy, Component, NgZone} from '@angular/core'; import {animate, state, style, transition, trigger} from '@angular/animations'; -import {MatDialog, MatDialogConfig} from '@angular/material/dialog'; +import {MatDialog} from '@angular/material/dialog'; import {MatSnackBar} from '@angular/material/snack-bar'; import {PhotosComponent} from '../../photos/photos.component'; import {Observable} from 'rxjs'; import {BreakpointObserver, Breakpoints, BreakpointState} from '@angular/cdk/layout'; import {Post} from '../../domain/post-state.model'; -import {DownloadPath} from '../../domain/download-path.model'; import {ConfirmDialogComponent} from '../../confirmation-component/confirmation-dialog'; import {filter, flatMap} from 'rxjs/operators'; import {RemoveResponse} from '../../domain/remove-response.model'; import {PostContextMenuService} from '../../services/post-context-menu.service'; import {PostsService} from '../../services/posts.service'; -import {AlternativeTitleComponent, AlternativeTitleDialog} from '../alternative-title/alternative-title.component'; import {PostId} from '../../domain/post-id.model'; @Component({ @@ -33,13 +30,11 @@ import {PostId} from '../../domain/post-id.model'; changeDetection: ChangeDetectionStrategy.OnPush }) export class PostContextMenuComponent { - post: Post; + post!: Post; isExtraSmall: Observable = this.breakpointObserver.observe(Breakpoints.XSmall); - fs; - contextMenuService: PostContextMenuService; + contextMenuService!: PostContextMenuService; constructor( - public electronService: ElectronService, private dialog: MatDialog, private breakpointObserver: BreakpointObserver, private httpClient: HttpClient, @@ -49,32 +44,25 @@ export class PostContextMenuComponent { private appService: AppService, private postsDataService: PostsService ) { - if (this.electronService.isElectronApp) { - this.fs = this.electronService.remote.require('fs'); - } } restart() { this.contextMenuService.closePostContextMenu(); this.httpClient.post(this.serverService.baseUrl + '/post/restart', [this.post.postId]).subscribe( - () => { - }, - error => { - this._snackBar.open(error?.error?.message || 'Unexpected error, check log file', null, { - duration: 5000 - }); + { + error: error => { + this._snackBar.open(error?.error?.message || 'Unexpected error, check log file', undefined, { + duration: 5000 + }); + } } ); } goTo() { this.contextMenuService.closePostContextMenu(); - if (this.electronService.isElectronApp) { - this.electronService.shell.openExternal(this.post.url); - } else { - window.open(this.post.url, '_blank'); - } + window.open(this.post.url, '_blank'); } remove() { @@ -92,61 +80,30 @@ export class PostContextMenuComponent { .pipe( filter(e => e === 'yes'), flatMap(() => - this.httpClient.post(this.serverService.baseUrl + '/post/remove', [this.post.postId]) + this.httpClient.post(this.serverService.baseUrl + '/post/remove', [this.post.postId]) ) ) - .subscribe( - data => { - this.postsDataService.remove(data); - }, - error => { - this._snackBar.open(error?.error?.message || 'Unexpected error, check log file', null, { - duration: 5000 - }); + .subscribe({ + next: data => { + this.postsDataService.remove(data); + }, error: error => { + this._snackBar.open(error?.error?.message || 'Unexpected error, check log file', undefined, { + duration: 5000 + }); + } } ); }); } - useAltTitle() { - this.contextMenuService.closePostContextMenu(); - this.httpClient.post(this.serverService.baseUrl + '/post/rename/first', [{ - postId: this.post.postId - }]).subscribe( - () => { - }, - error => { - this._snackBar.open(error?.error?.message || 'Unexpected error, check log file', null, { - duration: 5000 - }); - } - ); - } - - openRenameDialog() { - this.contextMenuService.closePostContextMenu(); - const dialogConfig: MatDialogConfig = { - maxHeight: '100vh', - maxWidth: '100vw', - height: '300px', - width: '60%', - data: {post: this.post} - }; - this.ngZone.run(() => { - this.dialog - .open(AlternativeTitleComponent, dialogConfig); - }); - } - stop() { this.contextMenuService.closePostContextMenu(); - this.httpClient.post(this.serverService.baseUrl + '/post/stop', [this.post.postId]).subscribe( - () => { - }, - error => { - this._snackBar.open(error?.error?.message || 'Unexpected error, check log file', null, { - duration: 5000 - }); + this.httpClient.post(this.serverService.baseUrl + '/post/stop', [this.post.postId]).subscribe({ + error: error => { + this._snackBar.open(error?.error?.message || 'Unexpected error, check log file', undefined, { + duration: 5000 + }); + } } ); } @@ -177,29 +134,4 @@ export class PostContextMenuComponent { }); }); } - - open() { - this.contextMenuService.closePostContextMenu(); - if (!this.electronService.isElectronApp) { - console.error('Cannot open downloader folder, not electron app'); - return; - } - // Request the server to give the correct file location - this.httpClient.get(this.serverService.baseUrl + '/post/path/' + this.post.postId).subscribe( - path => { - if (this.fs.existsSync(path.path)) { - this.electronService.shell.openPath(path.path).then(); - } else { - this._snackBar.open(path.path + ' does not exist, you probably removed it', null, { - duration: 5000 - }); - } - }, - (error: HttpErrorResponse) => { - this._snackBar.open(error?.error?.message || 'Unexpected error, check log file', null, { - duration: 5000 - }); - } - ); - } } diff --git a/vripper-web-ui/src/app/posts/posts.component.html b/vripper-web-ui/src/app/posts/posts.component.html new file mode 100644 index 00000000..ba1b0df4 --- /dev/null +++ b/vripper-web-ui/src/app/posts/posts.component.html @@ -0,0 +1,4 @@ +
+ + +
diff --git a/vripper-ui/src/app/posts/posts.component.ts b/vripper-web-ui/src/app/posts/posts.component.ts similarity index 84% rename from vripper-ui/src/app/posts/posts.component.ts rename to vripper-web-ui/src/app/posts/posts.component.ts index 8d495d74..9a081fad 100644 --- a/vripper-ui/src/app/posts/posts.component.ts +++ b/vripper-web-ui/src/app/posts/posts.component.ts @@ -1,5 +1,5 @@ import {SelectionService} from '../services/selection-service'; -import {ChangeDetectionStrategy, Component, NgZone, OnDestroy} from '@angular/core'; +import {ChangeDetectionStrategy, Component, NgZone, OnDestroy, ViewChild} from '@angular/core'; import {PostsDataSource} from './posts.datasource'; import {WsConnectionService} from '../services/ws-connection.service'; import {GridOptions} from 'ag-grid-community'; @@ -13,6 +13,7 @@ import {Overlay, OverlayPositionBuilder} from '@angular/cdk/overlay'; import {PostsService} from '../services/posts.service'; import {PostAddedRendererNative} from '../grid-custom-cells/post-added-renderer.native'; import {PostOrderRendererNative} from '../grid-custom-cells/post-order-renderer.native'; +import {AgGridAngular} from "ag-grid-angular"; @Component({ selector: 'app-posts', @@ -21,7 +22,8 @@ import {PostOrderRendererNative} from '../grid-custom-cells/post-order-renderer. }) export class PostsComponent implements OnDestroy { gridOptions: GridOptions; - dataSource: PostsDataSource; + dataSource!: PostsDataSource; + @ViewChild('agGrid') agGrid!: AgGridAngular; constructor( private wsConnection: WsConnectionService, @@ -37,7 +39,7 @@ export class PostsComponent implements OnDestroy { columnDefs: [ { headerName: 'Title', - field: 'title', + field: 'postTitle', cellRenderer: 'nativeTitleCellRenderer', cellRendererParams: { contextMenuService: this.contextMenuService, @@ -74,14 +76,6 @@ export class PostsComponent implements OnDestroy { contextMenuService: this.contextMenuService }, flex: 1 - }, { - headerName: 'Alternative titles', - field: 'metadata', - cellRenderer: 'nativeAltCellRenderer', - cellRendererParams: { - contextMenuService: this.contextMenuService - }, - flex: 1 }, { headerName: 'Added On', field: 'addedOn', @@ -110,7 +104,7 @@ export class PostsComponent implements OnDestroy { headerHeight: 35, animateRows: true, rowSelection: 'multiple', - rowDeselection: true, + suppressRowDeselection: false, rowData: [], components: { nativeProgressCellRenderer: PostProgressRendererNative, @@ -123,16 +117,15 @@ export class PostsComponent implements OnDestroy { }, overlayLoadingTemplate: '', overlayNoRowsTemplate: '', - getRowNodeId: data => data['postId'], + getRowId: row => row.data['postId'], onGridReady: () => { - this.dataSource = new PostsDataSource(this.wsConnection, this.gridOptions, this.zone); - this.postsDataService.setGridApi(this.gridOptions.api); + this.dataSource = new PostsDataSource(this.wsConnection, this.agGrid.api, this.zone); + this.postsDataService.setGridApi(this.agGrid.api); this.dataSource.connect(); }, - onSelectionChanged: () => this.selectionService.onSelectionChanged(this.gridOptions.api.getSelectedNodes()), + onSelectionChanged: () => this.selectionService.onSelectionChanged(this.agGrid.api.getSelectedNodes()), onBodyScroll: () => this.contextMenuService.closePostContextMenu(), - onRowDataUpdated: () => this.postsService.setCount(this.gridOptions.api.getDisplayedRowCount()), - onRowDataChanged: () => this.postsService.setCount(this.gridOptions.api.getDisplayedRowCount()), + onRowDataUpdated: () => this.postsService.setCount(this.agGrid.api.getDisplayedRowCount()), }; } diff --git a/vripper-ui/src/app/posts/posts.datasource.ts b/vripper-web-ui/src/app/posts/posts.datasource.ts similarity index 63% rename from vripper-ui/src/app/posts/posts.datasource.ts rename to vripper-web-ui/src/app/posts/posts.datasource.ts index 51e88e5c..dd5414c8 100644 --- a/vripper-ui/src/app/posts/posts.datasource.ts +++ b/vripper-web-ui/src/app/posts/posts.datasource.ts @@ -1,42 +1,42 @@ import {Post} from '../domain/post-state.model'; import {Subscription} from 'rxjs'; import {WsConnectionService} from '../services/ws-connection.service'; -import {GridOptions, RowNode} from 'ag-grid-community'; +import {GridApi, IRowNode} from 'ag-grid-community'; import {NgZone} from '@angular/core'; export class PostsDataSource { subscriptions: Subscription[] = []; - constructor(private ws: WsConnectionService, private gridOptions: GridOptions, private zone: NgZone) { + constructor(private ws: WsConnectionService, private gridApi: GridApi, private zone: NgZone) { } connect() { this.subscriptions.push(this.ws.posts$.subscribe((e: Post[]) => { this.zone.run(() => { - const toAdd = []; - const toUpdate = []; + const toAdd: Post[] = []; + const toUpdate: Post[] = []; e.forEach(v => { - if (this.gridOptions.api.getRowNode(v.postId) == null) { + if (this.gridApi.getRowNode(v.postId) == null) { toAdd.push(v); } else { toUpdate.push(v); } }); - this.gridOptions.api.applyTransaction({update: toUpdate, add: toAdd}); + this.gridApi.applyTransaction({update: toUpdate, add: toAdd}); }); })); this.subscriptions.push(this.ws.postsRemove$.subscribe((e: string[]) => { this.zone.run(() => { - const toRemove = []; + const toRemove: any[] = []; e.forEach(v => { - const rowNode: RowNode = this.gridOptions.api.getRowNode(v); + const rowNode: IRowNode | undefined = this.gridApi.getRowNode(v); if (rowNode != null) { toRemove.push(rowNode.data); } return; }); - this.gridOptions.api.applyTransaction({remove: toRemove}); + this.gridApi.applyTransaction({remove: toRemove}); }); })); } diff --git a/vripper-ui/src/app/preview-tooltip/preview-tooltip.component.ts b/vripper-web-ui/src/app/preview-tooltip/preview-tooltip.component.ts similarity index 100% rename from vripper-ui/src/app/preview-tooltip/preview-tooltip.component.ts rename to vripper-web-ui/src/app/preview-tooltip/preview-tooltip.component.ts diff --git a/vripper-ui/src/app/preview-tooltip/preview-tooltip.directive.ts b/vripper-web-ui/src/app/preview-tooltip/preview-tooltip.directive.ts similarity index 96% rename from vripper-ui/src/app/preview-tooltip/preview-tooltip.directive.ts rename to vripper-web-ui/src/app/preview-tooltip/preview-tooltip.directive.ts index b22ac7a9..8b016e85 100644 --- a/vripper-ui/src/app/preview-tooltip/preview-tooltip.directive.ts +++ b/vripper-web-ui/src/app/preview-tooltip/preview-tooltip.directive.ts @@ -6,8 +6,8 @@ import {ComponentPortal} from '@angular/cdk/portal'; @Directive({selector: '[appPreview]'}) export class AppPreviewDirective implements OnInit, OnDestroy { tooltipPortal = new ComponentPortal(AppPreviewComponent); - @Input() appPreview: string[]; - private overlayRef: OverlayRef; + @Input() appPreview!: string[]; + private overlayRef!: OverlayRef; constructor( private overlayPositionBuilder: OverlayPositionBuilder, diff --git a/vripper-ui/src/app/scan/scan.component.html b/vripper-web-ui/src/app/scan/scan.component.html similarity index 100% rename from vripper-ui/src/app/scan/scan.component.html rename to vripper-web-ui/src/app/scan/scan.component.html diff --git a/vripper-ui/src/app/scan/scan.component.scss b/vripper-web-ui/src/app/scan/scan.component.scss similarity index 100% rename from vripper-ui/src/app/scan/scan.component.scss rename to vripper-web-ui/src/app/scan/scan.component.scss diff --git a/vripper-ui/src/app/scan/scan.component.ts b/vripper-web-ui/src/app/scan/scan.component.ts similarity index 92% rename from vripper-ui/src/app/scan/scan.component.ts rename to vripper-web-ui/src/app/scan/scan.component.ts index 90d717ad..74fbc465 100644 --- a/vripper-ui/src/app/scan/scan.component.ts +++ b/vripper-web-ui/src/app/scan/scan.component.ts @@ -13,7 +13,7 @@ import {BehaviorSubject, Subject} from 'rxjs'; changeDetection: ChangeDetectionStrategy.OnPush }) export class ScanComponent implements OnInit, AfterViewInit { - input: string; + input!: string; loading: Subject = new BehaviorSubject(false); constructor( @@ -43,7 +43,7 @@ export class ScanComponent implements OnInit, AfterViewInit { processUrl(url: string) { this.loading.next(true); this.httpClient - .post<{ threadId: string; postId: string }>(this.serverService.baseUrl + '/post', {url: url}) + .post(this.serverService.baseUrl + '/post', {url: url}) .pipe( finalize(() => { this.close(); @@ -53,7 +53,7 @@ export class ScanComponent implements OnInit, AfterViewInit { .subscribe(response => { }, error => { - this._snackBar.open(error?.error?.message || 'Unexpected error, check log file', null, { + this._snackBar.open(error?.error?.message || 'Unexpected error, check log file', undefined, { duration: 5000 }); }); diff --git a/vripper-ui/src/app/services/app.service.ts b/vripper-web-ui/src/app/services/app.service.ts similarity index 97% rename from vripper-ui/src/app/services/app.service.ts rename to vripper-web-ui/src/app/services/app.service.ts index db980ff6..67f863ac 100644 --- a/vripper-ui/src/app/services/app.service.ts +++ b/vripper-web-ui/src/app/services/app.service.ts @@ -32,13 +32,13 @@ export class AppService { return this._darkTheme; } - private _settings: Settings; + private _settings!: Settings; get settings(): Settings { return {...this._settings}; } - private _renderer: Renderer2; + private _renderer!: Renderer2; set renderer(renderer: Renderer2) { this._renderer = renderer; diff --git a/vripper-ui/src/app/services/event-log.service.ts b/vripper-web-ui/src/app/services/event-log.service.ts similarity index 95% rename from vripper-ui/src/app/services/event-log.service.ts rename to vripper-web-ui/src/app/services/event-log.service.ts index d97824a1..e9c7502a 100644 --- a/vripper-ui/src/app/services/event-log.service.ts +++ b/vripper-web-ui/src/app/services/event-log.service.ts @@ -8,7 +8,7 @@ import {EventLogMessageDialogComponent} from '../event-log/message-dialog/event- providedIn: 'root' }) export class EventLogService { - private api: GridApi; + private api!: GridApi; private _count$: Subject = new BehaviorSubject(0); @@ -27,7 +27,7 @@ export class EventLogService { this.api = api; } - search(event) { + search(event: any) { if (this.api) { this.api.setQuickFilter(event); } diff --git a/vripper-ui/src/app/services/home-tabs.service.ts b/vripper-web-ui/src/app/services/home-tabs.service.ts similarity index 100% rename from vripper-ui/src/app/services/home-tabs.service.ts rename to vripper-web-ui/src/app/services/home-tabs.service.ts diff --git a/vripper-ui/src/app/services/link-collector.service.ts b/vripper-web-ui/src/app/services/link-collector.service.ts similarity index 92% rename from vripper-ui/src/app/services/link-collector.service.ts rename to vripper-web-ui/src/app/services/link-collector.service.ts index 11c2a258..f05c9a6e 100644 --- a/vripper-ui/src/app/services/link-collector.service.ts +++ b/vripper-web-ui/src/app/services/link-collector.service.ts @@ -6,7 +6,7 @@ import {GridApi} from 'ag-grid-community'; providedIn: 'root' }) export class LinkCollectorService { - private api: GridApi; + private api!: GridApi; private _count$: Subject = new BehaviorSubject(0); @@ -18,7 +18,7 @@ export class LinkCollectorService { this.api = api; } - search(event) { + search(event: any) { if (this.api) { this.api.setQuickFilter(event); } diff --git a/vripper-ui/src/app/services/multi-post.service.ts b/vripper-web-ui/src/app/services/multi-post.service.ts similarity index 100% rename from vripper-ui/src/app/services/multi-post.service.ts rename to vripper-web-ui/src/app/services/multi-post.service.ts diff --git a/vripper-ui/src/app/services/post-context-menu.service.ts b/vripper-web-ui/src/app/services/post-context-menu.service.ts similarity index 95% rename from vripper-ui/src/app/services/post-context-menu.service.ts rename to vripper-web-ui/src/app/services/post-context-menu.service.ts index ad77ba66..b8912a39 100644 --- a/vripper-ui/src/app/services/post-context-menu.service.ts +++ b/vripper-web-ui/src/app/services/post-context-menu.service.ts @@ -12,8 +12,8 @@ import {filter, take} from 'rxjs/operators'; export class PostContextMenuService { postContextMenuPortal = new ComponentPortal(PostContextMenuComponent); - postContext: Subscription; - private postContextMenuOverlayRef: OverlayRef; + postContext!: Subscription; + private postContextMenuOverlayRef: OverlayRef | null = null; constructor(private overlayPositionBuilder: OverlayPositionBuilder, private overlay: Overlay) { } diff --git a/vripper-ui/src/app/services/posts.service.ts b/vripper-web-ui/src/app/services/posts.service.ts similarity index 80% rename from vripper-ui/src/app/services/posts.service.ts rename to vripper-web-ui/src/app/services/posts.service.ts index c64f5579..9b245a6c 100644 --- a/vripper-ui/src/app/services/posts.service.ts +++ b/vripper-web-ui/src/app/services/posts.service.ts @@ -6,7 +6,7 @@ import {BehaviorSubject, Observable, Subject} from 'rxjs'; providedIn: 'root' }) export class PostsService { - private api: GridApi; + private api!: GridApi; private _count$: Subject = new BehaviorSubject(0); @@ -22,10 +22,10 @@ export class PostsService { this.api = api; } - public remove(toRemove: { postId: string }[]) { - const removeTx = []; + public remove(toRemove: string[]) { + const removeTx: any[] = []; toRemove.forEach(element => { - const nodeToDelete = this.api.getRowNode(element.postId); + const nodeToDelete = this.api.getRowNode(element); if (nodeToDelete != null) { removeTx.push(nodeToDelete.data); } @@ -33,7 +33,7 @@ export class PostsService { this.api.applyTransaction({remove: removeTx}); } - search(event) { + search(event: any) { if (this.api) { this.api.setQuickFilter(event); } diff --git a/vripper-ui/src/app/services/selection-service.ts b/vripper-web-ui/src/app/services/selection-service.ts similarity index 55% rename from vripper-ui/src/app/services/selection-service.ts rename to vripper-web-ui/src/app/services/selection-service.ts index 9323606f..c936fc91 100644 --- a/vripper-ui/src/app/services/selection-service.ts +++ b/vripper-web-ui/src/app/services/selection-service.ts @@ -1,19 +1,19 @@ import {Injectable} from '@angular/core'; import {Observable, Subject} from 'rxjs'; -import {RowNode} from 'ag-grid-community'; +import {IRowNode} from 'ag-grid-community'; @Injectable({ providedIn: 'root' }) export class SelectionService { - private _selected$: Subject = new Subject(); + private _selected$: Subject = new Subject(); - get selected$(): Observable { + get selected$(): Observable { return this._selected$.asObservable(); } - public onSelectionChanged(selected: RowNode[]) { + public onSelectionChanged(selected: IRowNode[]) { this._selected$.next(selected); } } diff --git a/vripper-ui/src/app/services/server-service.ts b/vripper-web-ui/src/app/services/server-service.ts similarity index 86% rename from vripper-ui/src/app/services/server-service.ts rename to vripper-web-ui/src/app/services/server-service.ts index 85a5c5cc..7229ca2e 100644 --- a/vripper-ui/src/app/services/server-service.ts +++ b/vripper-web-ui/src/app/services/server-service.ts @@ -8,7 +8,7 @@ export class ServerService { constructor() { } - private _baseUrl: string; + private _baseUrl!: string; get baseUrl(): string { return this._baseUrl; @@ -18,7 +18,7 @@ export class ServerService { this._baseUrl = baseUrl; } - private _wsBaseUrl: string; + private _wsBaseUrl!: string; get wsBaseUrl(): string { return this._wsBaseUrl; diff --git a/vripper-ui/src/app/services/ws-connection.service.ts b/vripper-web-ui/src/app/services/ws-connection.service.ts similarity index 70% rename from vripper-ui/src/app/services/ws-connection.service.ts rename to vripper-web-ui/src/app/services/ws-connection.service.ts index c9c38e04..084c0396 100644 --- a/vripper-ui/src/app/services/ws-connection.service.ts +++ b/vripper-web-ui/src/app/services/ws-connection.service.ts @@ -1,40 +1,35 @@ import {Injectable} from '@angular/core'; -import {BehaviorSubject, Observable, Subject, Subscription} from 'rxjs'; +import {BehaviorSubject, Observable, Subscription} from 'rxjs'; import {environment} from 'src/environments/environment'; import {ServerService} from './server-service'; import {MultiPostModel} from '../domain/multi-post.model'; import {WSMessage} from '../domain/ws-message.model'; import {Photo} from '../domain/photo-model'; import {Post} from '../domain/post-state.model'; -import {LoggedUser} from '../domain/logged-user.model'; -import {DownloadSpeed} from '../domain/download-speed.model'; import {GlobalState} from '../domain/global-state.model'; import {map, share} from 'rxjs/operators'; import {RxStomp, RxStompConfig, RxStompState} from '@stomp/rx-stomp'; -import {ElectronService} from 'ngx-electron'; import {EventLog} from '../domain/event.model'; @Injectable({ providedIn: 'root' }) export class WsConnectionService { - rxStomp: RxStomp; - private globalState: Observable; - private speed: Observable; - private user: Observable; - private posts: Observable>; - private events: Observable>; - private postDetails: Observable>; - private multiPostModels: Observable>; - private queuedRemove: Observable>; - private postsRemove: Observable>; - private eventsRemove: Observable>; - private postIdDetails: string; - - private connectionState$: Subject = new BehaviorSubject(RxStompState.CLOSED); - private stateSubscription: Subscription; - - constructor(private serverService: ServerService, private electronService: ElectronService) { + rxStomp!: RxStomp; + private globalState!: Observable; + private posts!: Observable>; + private events!: Observable>; + private postDetails!: Observable>; + private multiPostModels!: Observable>; + private queuedRemove!: Observable>; + private postsRemove!: Observable>; + private eventsRemove!: Observable>; + private postIdDetails!: string; + + private connectionState$ = new BehaviorSubject(RxStompState.CLOSED); + private stateSubscription!: Subscription; + + constructor(private serverService: ServerService) { this.init(); } @@ -46,14 +41,6 @@ export class WsConnectionService { return this.globalState; } - get speed$(): Observable { - return this.speed; - } - - get user$(): Observable { - return this.user; - } - get posts$(): Observable { return this.posts; } @@ -79,26 +66,10 @@ export class WsConnectionService { } init() { - if (this.electronService.isElectronApp) { - const portRequest = setInterval(() => { - this.electronService.ipcRenderer.send('get-port'); - }, 200); - - // wait for MainIPC - this.electronService.ipcRenderer.once('port', (event, port) => { - clearInterval(portRequest); - console.log('server running on port', port); - this.serverService.baseUrl = 'http://localhost:' + port; - this.serverService.wsBaseUrl = 'ws://localhost:' + port; - this.connect(); - this.subscribe(); - }); - } else { this.serverService.baseUrl = environment.localhost; this.serverService.wsBaseUrl = environment.ws; this.connect(); this.subscribe(); - } } connect() { @@ -146,7 +117,7 @@ export class WsConnectionService { share() ); - this.globalState = this.rxStomp.watch('/topic/download-state').pipe( + this.globalState = this.rxStomp.watch('/topic/state').pipe( map(e => { const state: GlobalState = JSON.parse(e.body); return state; @@ -154,20 +125,6 @@ export class WsConnectionService { share() ); - this.speed = this.rxStomp.watch('/topic/speed').pipe( - map(e => { - return JSON.parse(e.body); - }), - share() - ); - - this.user = this.rxStomp.watch('/topic/user').pipe( - map(e => { - return JSON.parse(e.body); - }), - share() - ); - this.posts = this.rxStomp.watch('/topic/posts').pipe( map(e => { const posts: Array = []; @@ -175,16 +132,13 @@ export class WsConnectionService { posts.push( new Post( element.postId, - element.title, + element.postTitle, element.done === 0 && element.total === 0 ? 0 : (element.done / element.total) * 100, element.status, - element.removed, element.url, element.done, element.total, element.hosts, - element.thanked, - element.previews, element.metadata, element.addedOn, element.rank + 1 diff --git a/vripper-ui/src/app/settings/settings.component.html b/vripper-web-ui/src/app/settings/settings.component.html similarity index 79% rename from vripper-ui/src/app/settings/settings.component.html rename to vripper-web-ui/src/app/settings/settings.component.html index 62e6a0c0..70d1d581 100644 --- a/vripper-ui/src/app/settings/settings.component.html +++ b/vripper-web-ui/src/app/settings/settings.component.html @@ -20,9 +20,6 @@

Preferences

required /> -
@@ -35,7 +32,7 @@

Preferences

- Save posts in sub folders Preferences Preferences
Select a proxy - + {{mirror}} - + Enable ViperGirls Authentication -
+
- +
- Send a like to poster + Send a like to poster
-
- Send a like - when download starts - - -
@@ -199,17 +188,6 @@

Preferences

- -
-
- Monitor Clipboard - - -
-
-
diff --git a/vripper-ui/src/app/settings/settings.component.scss b/vripper-web-ui/src/app/settings/settings.component.scss similarity index 100% rename from vripper-ui/src/app/settings/settings.component.scss rename to vripper-web-ui/src/app/settings/settings.component.scss diff --git a/vripper-ui/src/app/settings/settings.component.ts b/vripper-web-ui/src/app/settings/settings.component.ts similarity index 56% rename from vripper-ui/src/app/settings/settings.component.ts rename to vripper-web-ui/src/app/settings/settings.component.ts index 2ac0c0b3..8010a08c 100644 --- a/vripper-ui/src/app/settings/settings.component.ts +++ b/vripper-web-ui/src/app/settings/settings.component.ts @@ -1,15 +1,12 @@ import {finalize} from 'rxjs/operators'; import {AppService} from '../services/app.service'; -import {ClipboardService} from '../services/clipboard.service'; import {ChangeDetectionStrategy, Component, NgZone, OnDestroy, OnInit} from '@angular/core'; import {HttpClient} from '@angular/common/http'; import {FormControl, FormGroup} from '@angular/forms'; import {MatSnackBar} from '@angular/material/snack-bar'; import {ServerService} from '../services/server-service'; -import {ElectronService} from 'ngx-electron'; -import {Settings} from '../domain/settings.model'; +import {ConnectionSettings, DownloadSettings, Settings, ViperSettings} from '../domain/settings.model'; import {EMPTY, Observable, Subscription} from 'rxjs'; -import OpenDialogReturnValue = Electron.OpenDialogReturnValue; @Component({ selector: 'app-settings', @@ -26,29 +23,28 @@ export class SettingsComponent implements OnInit, OnDestroy { subscriptions: Subscription[] = []; viperGirlsSettingsForm = new FormGroup({ - vLogin: new FormControl(false), - vUsername: new FormControl(''), - vPassword: new FormControl(''), - vThanks: new FormControl(false), - leaveThanksOnStart: new FormControl(false), - vProxy: new FormControl('') + login: new FormControl(false), + username: new FormControl(''), + password: new FormControl(''), + thanks: new FormControl(false), + host: new FormControl('') }); downloadSettingsForm = new FormGroup({ downloadPath: new FormControl(''), autoStart: new FormControl(false), forceOrder: new FormControl(false), - subLocation: new FormControl(false), + forumSubfolder: new FormControl(false), threadSubLocation: new FormControl(false), clearCompleted: new FormControl(false), appendPostId: new FormControl(false) }); connectionSettingsForm = new FormGroup({ - maxThreads: new FormControl(''), - maxTotalThreads: new FormControl(''), - connectionTimeout: new FormControl(''), - maxAttempts: new FormControl('') + maxThreads: new FormControl(0), + maxTotalThreads: new FormControl(0), + timeout: new FormControl(0), + maxAttempts: new FormControl(0) }); desktopSettingsForm = new FormGroup({ @@ -56,27 +52,16 @@ export class SettingsComponent implements OnInit, OnDestroy { }); eventLogSettingsForm = new FormGroup({ - maxEventLog: new FormControl('') + maxEventLog: new FormControl(0) }); constructor( private httpClient: HttpClient, private _snackBar: MatSnackBar, private serverService: ServerService, - public electronService: ElectronService, - private clipboardService: ClipboardService, private appService: AppService, private zone: NgZone ) { - this.subscriptions.push(this.viperGirlsSettingsForm.get('vThanks').valueChanges.subscribe(c => { - const formControl: FormControl = this.viperGirlsSettingsForm.get('leaveThanksOnStart') as FormControl; - if (!c) { - formControl.setValue(false); - formControl.disable(); - } else { - formControl.enable(); - } - })); } updateTheme() { @@ -92,13 +77,13 @@ export class SettingsComponent implements OnInit, OnDestroy { this.mirrors = this.httpClient.get(this.serverService.baseUrl + '/settings/proxies'); this.httpClient.get(this.serverService.baseUrl + '/settings') .subscribe(data => { - this.viperGirlsSettingsForm.reset(data); - this.downloadSettingsForm.reset(data); - this.connectionSettingsForm.reset(data); + this.viperGirlsSettingsForm.reset(data.viperSettings); + this.downloadSettingsForm.reset(data.downloadSettings); + this.connectionSettingsForm.reset(data.connectionSettings); this.desktopSettingsForm.reset(data); this.eventLogSettingsForm.reset(data); }, error => { - this._snackBar.open(error?.error?.message || 'Unexpected error, check log file', null, { + this._snackBar.open(error?.error?.message || 'Unexpected error, check log file', undefined, { duration: 5000 }); }); @@ -108,49 +93,32 @@ export class SettingsComponent implements OnInit, OnDestroy { this.subscriptions.forEach(s => s.unsubscribe()); } - browse() { - this.electronService.remote.dialog - .showOpenDialog(this.electronService.remote.getCurrentWindow(), { - properties: ['openDirectory'] - }) - .then((value: OpenDialogReturnValue) => { - this.zone.run(() => { - if (!value.canceled && value.filePaths !== undefined) { - this.downloadSettingsForm.get('downloadPath').setValue(value.filePaths[0]); - this.downloadSettingsForm.get('downloadPath').markAsDirty(); - this.downloadSettingsForm.get('downloadPath').markAsTouched(); - } - }); - }); - } - onSubmit(): void { this.loading = true; this.httpClient .post(this.serverService.baseUrl + '/settings', { - ...this.viperGirlsSettingsForm.value, - ...this.downloadSettingsForm.value, - ...this.connectionSettingsForm.value, + viperSettings: {...this.viperGirlsSettingsForm.value as ViperSettings}, + connectionSettings: {...this.connectionSettingsForm.value as ConnectionSettings}, + downloadSettings: {...this.downloadSettingsForm.value as DownloadSettings}, darkTheme: this.darkTheme, ...this.desktopSettingsForm.value, ...this.eventLogSettingsForm.value - }) + } as Settings) .pipe(finalize(() => (this.loading = false))) .subscribe( data => { - this._snackBar.open('Settings updated', null, { + this._snackBar.open('Settings updated', undefined, { duration: 5000 }); - this.viperGirlsSettingsForm.reset(data); - this.downloadSettingsForm.reset(data); - this.connectionSettingsForm.reset(data); + this.viperGirlsSettingsForm.reset(data.viperSettings); + this.downloadSettingsForm.reset(data.downloadSettings); + this.connectionSettingsForm.reset(data.connectionSettings); this.desktopSettingsForm.reset(data); this.eventLogSettingsForm.reset(data); - this.clipboardService.init(data); this.updateSettings(data); }, error => { - this._snackBar.open(error?.error?.message || 'Unexpected error, check log file', null, { + this._snackBar.open(error?.error?.message || 'Unexpected error, check log file', undefined, { duration: 5000 }); } diff --git a/vripper-ui/src/app/status-bar/status-bar.component.html b/vripper-web-ui/src/app/status-bar/status-bar.component.html similarity index 82% rename from vripper-ui/src/app/status-bar/status-bar.component.html rename to vripper-web-ui/src/app/status-bar/status-bar.component.html index 6508c8b2..d9888b56 100644 --- a/vripper-ui/src/app/status-bar/status-bar.component.html +++ b/vripper-web-ui/src/app/status-bar/status-bar.component.html @@ -1,8 +1,8 @@
Selected: {{length}} - {{ speed.speed + '/s' }} + {{ state.downloadSpeed + '/s' }} Downloading: {{ state!.running }} Pending: {{ state!.remaining }} Error: {{ state!.error }} diff --git a/vripper-ui/src/app/status-bar/status-bar.component.scss b/vripper-web-ui/src/app/status-bar/status-bar.component.scss similarity index 100% rename from vripper-ui/src/app/status-bar/status-bar.component.scss rename to vripper-web-ui/src/app/status-bar/status-bar.component.scss diff --git a/vripper-web-ui/src/app/status-bar/status-bar.component.scss-theme.scss b/vripper-web-ui/src/app/status-bar/status-bar.component.scss-theme.scss new file mode 100644 index 00000000..0e375cf4 --- /dev/null +++ b/vripper-web-ui/src/app/status-bar/status-bar.component.scss-theme.scss @@ -0,0 +1,12 @@ +@use 'sass:map'; +@use '@angular/material' as mat; + +@mixin status-bar-component-theme($theme) { + + $theme-color: mat.get-color-config($theme); + $primary: map.get($theme-color, 'primary'); + + .status-bar { + color: mat.get-color-from-palette($primary, '900-contrast'); + } +} diff --git a/vripper-ui/src/app/status-bar/status-bar.component.ts b/vripper-web-ui/src/app/status-bar/status-bar.component.ts similarity index 81% rename from vripper-ui/src/app/status-bar/status-bar.component.ts rename to vripper-web-ui/src/app/status-bar/status-bar.component.ts index 357a81f9..f1f860df 100644 --- a/vripper-ui/src/app/status-bar/status-bar.component.ts +++ b/vripper-web-ui/src/app/status-bar/status-bar.component.ts @@ -1,6 +1,5 @@ import {SelectionService} from '../services/selection-service'; import {ChangeDetectionStrategy, Component, NgZone, OnDestroy, OnInit} from '@angular/core'; -import {DownloadSpeed} from '../domain/download-speed.model'; import {WsConnectionService} from '../services/ws-connection.service'; import {BehaviorSubject, Subject, Subscription} from 'rxjs'; import {GlobalState} from '../domain/global-state.model'; @@ -12,8 +11,7 @@ import {GlobalState} from '../domain/global-state.model'; changeDetection: ChangeDetectionStrategy.OnPush }) export class StatusBarComponent implements OnInit, OnDestroy { - downloadSpeed$: Subject = new BehaviorSubject(new DownloadSpeed('0 B')); - globalState$: Subject = new BehaviorSubject(new GlobalState(0, 0, 0)); + globalState$: Subject = new BehaviorSubject(new GlobalState(0, 0, 0, '', '')); selected$: Subject = new BehaviorSubject(0); subscriptions: Subscription[] = []; @@ -21,7 +19,6 @@ export class StatusBarComponent implements OnInit, OnDestroy { } ngOnInit() { - this.subscriptions.push(this.ws.speed$.subscribe(e => this.ngZone.run(() => this.downloadSpeed$.next(e)))); this.subscriptions.push(this.ws.globalState$.subscribe(e => this.ngZone.run(() => this.globalState$.next(e)))); this.subscriptions.push(this.selectionService.selected$.subscribe(selected => this.ngZone.run(() => this.selected$.next(selected.length)))); } diff --git a/vripper-ui/src/app/toolbar/toolbar.component.html b/vripper-web-ui/src/app/toolbar/toolbar.component.html similarity index 90% rename from vripper-ui/src/app/toolbar/toolbar.component.html rename to vripper-web-ui/src/app/toolbar/toolbar.component.html index 054c409e..e1431ff9 100644 --- a/vripper-ui/src/app/toolbar/toolbar.component.html +++ b/vripper-web-ui/src/app/toolbar/toolbar.component.html @@ -17,11 +17,6 @@ matTooltip="Stop selected"> pause -
@@ -54,7 +49,7 @@

- Logged in as: {{ user.user }} + Logged in as: {{ user }}

NJZX`o!t)&4rl?})e5?CblpVQsUZAHv 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 P3uJWOOs`hQDdztHz+chH diff --git a/vripper-electron/build/icons/64x64.png b/vripper-electron/build/icons/64x64.png deleted file mode 100644 index 8c1e926d560dfc30641b9d1dc5c007b57ff226b7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2103 zcmV-72*~$|P)l#K4Q-X!v5BIF05Zf$-6)Q5MF~@H%&;q@$OC^?p=4i-o0}_X5FU$r}dpPXU-hYnK^UL89|wG1e(&YML=s7ngy%_ z8i0DB#v;28B!Ps0NeQ1xm_`Fq zP$}Ro2~YVFZROCHfE~MFK}D!kKtEIh?_q9qw-b&fV5>?AwtIjCA>jbG-{b+lABMO0 zD&}Z=47R{;1?|s6@b0ld#TK4Rr`tG}JX});=@c<`Af^=80qyqq4Fzk(3<0_g-#Zx#E=VbP(2WHDeDzfeBb%o}=Y@&HW>@QeHpiwZahbrOE&N=hI$G^C`7cu+v3 zvr|bEbue}j?EW*<=KfezKoENIaG!Ugfcgg>prWQmNtcgC2MTa9b`gYAurv3^q5=dw z>xEnV#6F>MK=^@f18IylKphN%-L zY-wuOt|ipeWJ{xRfMP>K$gHDyrxF6z8MyHMRZyCN5HvpWNXh0vbu|r}Hu)RF9U=yK zez9Lf2M3k^9lMRNVe@9&3y{oYh>eVRmahnE4gQm&_Knoc49RonY-#NF0hRqb*=Jm$ z)^%n`1(k;uZfnB`g^ISR6rg$e1Ewc)O6_@$GVLh39d4;Z*~f} zarrV=FKD+?fD>Sa#02!|<}R~YVm4tbVHh;Fw_}7t)Z1KuqQ{Qu7A=Q9uoPfbU}{&h z@;L11AiQ}qfvPIcJUrrJH;hAGUO*Nk4Rs(kJdDg_O13w4bl4t$*;I<@lP7hH1@Qaa zk3|Jk!G3&JT^8cj+#GYCep<43Rq_1nessPM|^0MEH3b1N#f2Vsi zy9-Q3O%308taL;^cKo<*aggw8ex31>>+k}+3fSLdTta2TqN1W?TN!SDQv&woe^{b` zr_Cbd7j=%9ICqZuXw-GTuP$BU+N5?b{e^B=;!-XPh;+l7z(?AKl+sfeD|+;(HW4g* z&;%0GTy2#oC;2FGrbX_cs}UpNJwZf65zvTMopZgsWpzk6)v)86_T$vQz55Wfb6SrTz@X-W3>8n^8U>7Xh0gnrF zCEOMeaL~!*Em~)gAP&Pz5`N`Rys|<{c)1&1EVknpgX_A0L-2&P8dmCsm{a^q!p?0j z3%orQM*I=(%fMT}x4iT(13mzDbi*aL9X(fuSSIm|gqJbPZ2lXV1Ag5F2b?bXLijF? zc@OHX^3&azwZ4Y^yH51-KyL4FLzbHRUwR@_70% zJSd^ZdOUqU9^NNf$O7jvgSLISFqTe~MFIKfgGkV { - dialog.showErrorBox(err.message, err.stack); - process.exit(1); -}); - -createWindow = () => { - - if (process.platform === 'win32') { - app.setAppUserModelId('tn.mnlr.vripper'); - } - - let icon; - if (process.platform === 'win32') { - icon = __dirname + '/icon.ico'; - } else if (process.platform === 'linux') { - icon = __dirname + '/icon.png'; - } - - const mainWindowState = windowStateKeeper({ - defaultWidth: 1024, - defaultHeight: 800 - }); - - win = new BrowserWindow({ - x: mainWindowState.x, - y: mainWindowState.y, - width: mainWindowState.width, - height: mainWindowState.height, - minWidth: 800, - minHeight: 600, - webPreferences: { - nodeIntegration: true, - enableRemoteModule: true - }, - icon: icon - }); - - mainWindowState.manage(win); - win.removeMenu(); - win.setMenu(null); - win.setMenuBarVisibility(false); - - win.loadURL( - url.format({ - pathname: path.join(__dirname, `vripper-ui/index.html`), - protocol: "file:", - slashes: true - }) - ); - - // win.webContents.openDevTools(); - - win.on("closed", () => { - win = null; - }); -} - -shutdownServer = () => { - axios.post('http://localhost:' + serverPort + '/actuator/shutdown', {}, { - headers: {'content-type': 'application/json'}, - }).then((response) => { - terminationInteval = setInterval(() => { - terminationAttemps++; - if (terminated) { - console.log('viper server terminated'); - clearInterval(terminationInteval); - app.quit(); - } else if (terminationAttemps > maxTerminationAttemps) { - console.log('viper server is not terminated'); - console.log('Proceed to kill'); - vripperServer.kill('SIGKILL'); - clearInterval(terminationInteval); - app.quit(); - } - }, 1000); - }).catch((error) => { - // Terminate immediately - console.log(error); - vripperServer.kill('SIGKILL'); - terminated = true; - app.quit(); - }); -} - -const gotTheLock = app.requestSingleInstanceLock(); - -if (!gotTheLock) { - app.quit(); -} else { - getPort().then(port => { - serverPort = port; - ipcMain.on("get-port", event => { - event.reply("port", port); - }); - - const appPath = path.join(app.getAppPath(), '../../'); - let javaBinPath, jarPath, baseDir; - if (appImageDir !== undefined && process.platform === 'linux') { - javaBinPath = path.join(appImageDir, "java-runtime/bin/java"); - jarPath = path.join(appImageDir, "bin/vripper-server.jar"); - baseDir = path.join(appImagePath, '..'); - } else if (process.platform === 'darwin') { - javaBinPath = path.join(appPath, "java-runtime/bin/java"); - jarPath = path.join(appPath, "bin/vripper-server.jar"); - baseDir = app.getPath('appData'); - } else if (process.platform === 'win32') { - javaBinPath = path.join(appPath, "java-runtime/bin/java"); - jarPath = path.join(appPath, "bin/vripper-server.jar"); - baseDir = appPath; - } else { - console.error(`Unknown platform ${process.platform}`); - app.quit(); - } - - vripperServer = spawn(javaBinPath, [ - "-Xms256m", - "-Dvripper.server.port=" + port, - "-Dbase.dir=" + baseDir, - "-Djava.net.preferIPv4Stack=true", - "-jar", - jarPath - ], { - stdio: 'ignore' - }); - vripperServer.on('exit', (code, signal) => { - console.log(`vripper server terminated, code = ${code}, signal = ${signal}`); - terminated = true; - app.quit(); - }); - }); - - app.on("second-instance", (event, commandLine, workingDirectory) => { - if (win) { - if (win.isMinimized()) win.restore(); - win.focus(); - } - }); - - app.on("ready", createWindow); - - app.on("window-all-closed", () => { - if (process.platform !== "darwin") { - shutdownServer(); - } - }); - - app.on("will-quit", () => { - if (process.platform === "darwin") { - shutdownServer(); - } - }); - - app.on("activate", () => { - if (win === null) { - createWindow(); - } - }); -} diff --git a/vripper-electron/package.json b/vripper-electron/package.json deleted file mode 100644 index 184f9d27..00000000 --- a/vripper-electron/package.json +++ /dev/null @@ -1,84 +0,0 @@ -{ - "name": "vripper-electron", - "version": "3.5.4", - "description": "A ripper for vipergirls.to built using web technolgies", - "main": "main.js", - "author": "death-claw <53543762+death-claw@users.noreply.github.com>", - "homepage": "https://github.com/death-claw/vripper-project", - "license": "ISC", - "build": { - "appId": "tn.mnlr.vripper", - "productName": "VRipper", - "files": [ - "**/*", - "!**/node_modules/*/{CHANGELOG.md,README.md,README,readme.md,readme}", - "!**/node_modules/*/{test,__tests__,tests,powered-test,example,examples}", - "!**/node_modules/*.d.ts", - "!**/node_modules/.bin", - "!**/*.{iml,o,hprof,orig,pyc,pyo,rbc,swp,csproj,sln,xproj}", - "!.editorconfig", - "!**/._*", - "!**/{.DS_Store,.git,.hg,.svn,CVS,RCS,SCCS,.gitignore,.gitattributes}", - "!**/{__pycache__,thumbs.db,.flowconfig,.idea,.vs,.nyc_output}", - "!**/{appveyor.yml,.travis.yml,circle.yml}", - "!**/{npm-debug.log,yarn.lock,.yarn-integrity,.yarn-metadata.json}", - "!**/build-dir", - "!**/java-runtime", - "!**/pre-build.js", - "!**/pom.xml", - { - "from": "./build/", - "to": "." - } - ], - "extraFiles": [ - { - "from": "../vripper-server/target/vripper-server-${version}-electron.jar", - "to": "bin/vripper-server.jar" - }, - { - "from": "java-runtime", - "to": "java-runtime" - } - ], - "win": { - "icon": "icon.ico", - "target": [ - "dir" - ] - }, - "linux": { - "synopsis": "vipergirls.to ripper", - "category": "Utility", - "packageCategory": "Utility", - "target": [ - "AppImage" - ] - }, - "mac": { - "category": "public.app-category.utilities", - "target": [ - "dmg" - ], - "icon": "icon.icns" - } - }, - "scripts": { - "start": "electron .", - "dist": "node pre-build.js && electron-builder" - }, - "devDependencies": { - "electron": "11.1.1", - "electron-builder": "22.9.1" - }, - "dependencies": { - "axios": "0.21.1", - "cheerio": "1.0.0-rc.3", - "copy-dir": "1.3.0", - "electron-context-menu": "2.1.0", - "electron-window-state": "^5.0.3", - "get-port": "5.1.1", - "rimraf": "3.0.2", - "v8-compile-cache": "2.1.1" - } -} diff --git a/vripper-electron/pom.xml b/vripper-electron/pom.xml deleted file mode 100644 index d812877d..00000000 --- a/vripper-electron/pom.xml +++ /dev/null @@ -1,77 +0,0 @@ - - - 4.0.0 - - tn.mnlr - vripper - 3.5.4 - - vripper-electron - vripper-electron - - - - maven-jar-plugin - - ${buildClassifier} - - - - - - - electron - - electron - - - - tn.mnlr - vripper-server - ${project.version} - runtime - ${buildClassifier} - - - - - - com.github.eirslett - frontend-maven-plugin - 1.11.2 - - v14.16.0 - v1.22.10 - build-dir - - - - install node and yarn - - install-node-and-yarn - - - - yarn install - - yarn - - - - yarn run build - - yarn - - - run dist - - - - - - - - - diff --git a/vripper-electron/pre-build.js b/vripper-electron/pre-build.js deleted file mode 100644 index 829a5240..00000000 --- a/vripper-electron/pre-build.js +++ /dev/null @@ -1,25 +0,0 @@ -const cheerio = require('cheerio'); -const fs = require('fs'); -const rimraf = require("rimraf"); -const copydir = require("copy-dir"); -const {execSync} = require("child_process"); - -rimraf.sync('./build/vripper-ui'); -copydir.sync('../vripper-ui/dist/vripper-ui', './build/vripper-ui', {}); - - -const index = fs.readFileSync('./build/vripper-ui/index.html'); - - -const $ = cheerio.load(index); -$('body').prepend(``); - -fs.writeFileSync('./build/vripper-ui/index.html', $.html()); - -console.log('Building runtime environment'); -rimraf.sync('java-runtime'); -execSync('jlink --no-header-files --no-man-pages --compress=2 --strip-debug --add-modules java.base,java.desktop,java.instrument,java.management,java.security.jgss,java.sql,jdk.unsupported,jdk.crypto.ec --output java-runtime'); -if (process.platform === 'linux') { - console.log('Stripping libjvm.so'); - execSync('strip -p --strip-unneeded java-runtime/lib/server/libjvm.so'); -} diff --git a/vripper-electron/renderer.js b/vripper-electron/renderer.js deleted file mode 100644 index d4001875..00000000 --- a/vripper-electron/renderer.js +++ /dev/null @@ -1,3 +0,0 @@ -console.log("renderer intialized"); -const contextMenu = require("electron-context-menu"); -contextMenu({}); \ No newline at end of file diff --git a/vripper-electron/vripper-electron.iml b/vripper-electron/vripper-electron.iml deleted file mode 100644 index f409c0ea..00000000 --- a/vripper-electron/vripper-electron.iml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/vripper-electron/yarn.lock b/vripper-electron/yarn.lock deleted file mode 100644 index ae5b1b68..00000000 --- a/vripper-electron/yarn.lock +++ /dev/null @@ -1,1888 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"7zip-bin@~5.0.3": - version "5.0.3" - resolved "https://registry.yarnpkg.com/7zip-bin/-/7zip-bin-5.0.3.tgz" - integrity sha512-GLyWIFBbGvpKPGo55JyRZAo4lVbnBiD52cKlw/0Vt+wnmKvWJkpZvsjVoaIolyBXDeAQKSicRtqFNPem9w0WYA== - -"@develar/schema-utils@~2.6.5": - version "2.6.5" - resolved "https://registry.yarnpkg.com/@develar/schema-utils/-/schema-utils-2.6.5.tgz" - integrity sha512-0cp4PsWQ/9avqTVMCtZ+GirikIA36ikvjtHweU4/j8yLtgObI0+JUPhYFScgwlteveGB1rt3Cm8UhN04XayDig== - dependencies: - ajv "^6.12.0" - ajv-keywords "^3.4.1" - -"@electron/get@^1.0.1": - version "1.12.4" - resolved "https://registry.yarnpkg.com/@electron/get/-/get-1.12.4.tgz" - integrity sha512-6nr9DbJPUR9Xujw6zD3y+rS95TyItEVM0NVjt1EehY2vUWfIgPiIPVHxCvaTS0xr2B+DRxovYVKbuOWqC35kjg== - dependencies: - debug "^4.1.1" - env-paths "^2.2.0" - fs-extra "^8.1.0" - got "^9.6.0" - progress "^2.0.3" - semver "^6.2.0" - sumchecker "^3.0.1" - optionalDependencies: - global-agent "^2.0.2" - global-tunnel-ng "^2.7.1" - -"@sindresorhus/is@^0.14.0": - version "0.14.0" - resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.14.0.tgz" - integrity sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ== - -"@szmarczak/http-timer@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@szmarczak/http-timer/-/http-timer-1.1.2.tgz" - integrity sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA== - dependencies: - defer-to-connect "^1.0.1" - -"@types/debug@^4.1.5": - version "4.1.5" - resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.5.tgz" - integrity sha512-Q1y515GcOdTHgagaVFhHnIFQ38ygs/kmxdNpvpou+raI9UO3YZcHDngBSYKQklcKlvA7iuQlmIKbzvmxcOE9CQ== - -"@types/fs-extra@^9.0.1": - version "9.0.8" - resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-9.0.8.tgz" - integrity sha512-bnlTVTwq03Na7DpWxFJ1dvnORob+Otb8xHyUqUWhqvz/Ksg8+JXPlR52oeMSZ37YEOa5PyccbgUNutiQdi13TA== - dependencies: - "@types/node" "*" - -"@types/node@*": - version "14.14.34" - resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.34.tgz" - integrity sha512-dBPaxocOK6UVyvhbnpFIj2W+S+1cBTkHQbFQfeeJhoKFbzYcVUGHvddeWPSucKATb3F0+pgDq0i6ghEaZjsugA== - -"@types/node@^12.0.12": - version "12.20.5" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.5.tgz" - integrity sha512-5Oy7tYZnu3a4pnJ//d4yVvOImExl4Vtwf0D40iKUlU+XlUsyV9iyFWyCFlwy489b72FMAik/EFwRkNLjjOdSPg== - -"@types/yargs-parser@*": - version "20.2.0" - resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.0.tgz" - integrity sha512-37RSHht+gzzgYeobbG+KWryeAW8J33Nhr69cjTqSYymXVZEN9NbRYWoYlRtDhHKPVT1FyNKwaTPC1NynKZpzRA== - -"@types/yargs@^15.0.5": - version "15.0.13" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.13.tgz" - integrity sha512-kQ5JNTrbDv3Rp5X2n/iUu37IJBDU2gsZ5R/g1/KHOOEc5IKfUFjXT6DENPGduh08I/pamwtEq4oul7gUqKTQDQ== - dependencies: - "@types/yargs-parser" "*" - -ajv-keywords@^3.4.1: - version "3.5.2" - resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz" - integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== - -ajv@^6.12.0: - version "6.12.6" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz" - integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== - 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" - -ansi-align@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.0.tgz" - integrity sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw== - dependencies: - string-width "^3.0.0" - -ansi-regex@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz" - integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== - -ansi-regex@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz" - integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== - -ansi-styles@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz" - integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== - dependencies: - color-convert "^1.9.0" - -ansi-styles@^4.0.0, ansi-styles@^4.1.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz" - integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== - dependencies: - color-convert "^2.0.1" - -app-builder-bin@3.5.10: - version "3.5.10" - resolved "https://registry.yarnpkg.com/app-builder-bin/-/app-builder-bin-3.5.10.tgz" - integrity sha512-Jd+GW68lR0NeetgZDo47PdWBEPdnD+p0jEa7XaxjRC8u6Oo/wgJsfKUkORRgr2NpkD19IFKN50P6JYy04XHFLQ== - -app-builder-lib@22.9.1: - version "22.9.1" - resolved "https://registry.yarnpkg.com/app-builder-lib/-/app-builder-lib-22.9.1.tgz" - integrity sha512-KfXim/fiNwFW2SKffsjEMdAU7RbbEXn62x5YyXle1b4j9X/wEHW9iwox8De6y0hJdR+/kCC/49lI+VgNwLhV7A== - dependencies: - "7zip-bin" "~5.0.3" - "@develar/schema-utils" "~2.6.5" - async-exit-hook "^2.0.1" - bluebird-lst "^1.0.9" - builder-util "22.9.1" - builder-util-runtime "8.7.2" - chromium-pickle-js "^0.2.0" - debug "^4.3.0" - ejs "^3.1.5" - electron-publish "22.9.1" - fs-extra "^9.0.1" - hosted-git-info "^3.0.5" - is-ci "^2.0.0" - isbinaryfile "^4.0.6" - js-yaml "^3.14.0" - lazy-val "^1.0.4" - minimatch "^3.0.4" - normalize-package-data "^2.5.0" - read-config-file "6.0.0" - sanitize-filename "^1.6.3" - semver "^7.3.2" - temp-file "^3.3.7" - -argparse@^1.0.7: - version "1.0.10" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz" - integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== - dependencies: - sprintf-js "~1.0.2" - -astral-regex@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz" - integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== - -async-exit-hook@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/async-exit-hook/-/async-exit-hook-2.0.1.tgz" - integrity sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw== - -async@0.9.x: - version "0.9.2" - resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz" - integrity sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0= - -at-least-node@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz" - integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== - -axios@0.21.1: - version "0.21.1" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.21.1.tgz" - integrity sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA== - dependencies: - follow-redirects "^1.10.0" - -balanced-match@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz" - integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= - -bluebird-lst@^1.0.9: - version "1.0.9" - resolved "https://registry.yarnpkg.com/bluebird-lst/-/bluebird-lst-1.0.9.tgz" - integrity sha512-7B1Rtx82hjnSD4PGLAjVWeYH3tHAcVUmChh85a3lltKQm6FresXh9ErQo6oAv6CqxttczC3/kEg8SY5NluPuUw== - dependencies: - bluebird "^3.5.5" - -bluebird@^3.5.5: - version "3.7.2" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz" - integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== - -boolbase@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz" - integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= - -boolean@^3.0.1: - version "3.0.2" - resolved "https://registry.yarnpkg.com/boolean/-/boolean-3.0.2.tgz" - integrity sha512-RwywHlpCRc3/Wh81MiCKun4ydaIFyW5Ea6JbL6sRCVx5q5irDw7pMXBUFYF/jArQ6YrG36q0kpovc9P/Kd3I4g== - -boxen@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/boxen/-/boxen-4.2.0.tgz" - integrity sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ== - dependencies: - ansi-align "^3.0.0" - camelcase "^5.3.1" - chalk "^3.0.0" - cli-boxes "^2.2.0" - string-width "^4.1.0" - term-size "^2.1.0" - type-fest "^0.8.1" - widest-line "^3.1.0" - -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -buffer-crc32@~0.2.3: - version "0.2.13" - resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz" - integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= - -buffer-from@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz" - integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A== - -builder-util-runtime@8.7.2: - version "8.7.2" - resolved "https://registry.yarnpkg.com/builder-util-runtime/-/builder-util-runtime-8.7.2.tgz" - integrity sha512-xBqv+8bg6cfnzAQK1k3OGpfaHg+QkPgIgpEkXNhouZ0WiUkyZCftuRc2LYzQrLucFywpa14Xbc6+hTbpq83yRA== - dependencies: - debug "^4.1.1" - sax "^1.2.4" - -builder-util@22.9.1: - version "22.9.1" - resolved "https://registry.yarnpkg.com/builder-util/-/builder-util-22.9.1.tgz" - integrity sha512-5hN/XOaYu4ZQUS6F+5CXE6jTo+NAnVqAxDuKGSaHWb9bejfv/rluChTLoY3/nJh7RFjkoyVjvFJv7zQDB1QmHw== - dependencies: - "7zip-bin" "~5.0.3" - "@types/debug" "^4.1.5" - "@types/fs-extra" "^9.0.1" - app-builder-bin "3.5.10" - bluebird-lst "^1.0.9" - builder-util-runtime "8.7.2" - chalk "^4.1.0" - debug "^4.3.0" - fs-extra "^9.0.1" - is-ci "^2.0.0" - js-yaml "^3.14.0" - source-map-support "^0.5.19" - stat-mode "^1.0.0" - temp-file "^3.3.7" - -cacheable-request@^6.0.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-6.1.0.tgz" - integrity sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg== - dependencies: - clone-response "^1.0.2" - get-stream "^5.1.0" - http-cache-semantics "^4.0.0" - keyv "^3.0.0" - lowercase-keys "^2.0.0" - normalize-url "^4.1.0" - responselike "^1.0.2" - -camelcase@^5.3.1: - version "5.3.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz" - integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== - -chalk@^2.4.2: - version "2.4.2" - resolved "https://registry.yarnpkg.com/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" - -chalk@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz" - integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -chalk@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz" - integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -cheerio@1.0.0-rc.3: - version "1.0.0-rc.3" - resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.3.tgz" - integrity sha512-0td5ijfUPuubwLUu0OBoe98gZj8C/AA+RW3v67GPlGOrvxWjZmBXiBCRU+I8VEiNyJzjth40POfHiz2RB3gImA== - dependencies: - css-select "~1.2.0" - dom-serializer "~0.1.1" - entities "~1.1.1" - htmlparser2 "^3.9.1" - lodash "^4.15.0" - parse5 "^3.0.1" - -chromium-pickle-js@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz" - integrity sha1-BKEGZywYsIWrd02YPfo+oTjyIgU= - -ci-info@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-2.0.0.tgz" - integrity sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ== - -cli-boxes@^2.2.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/cli-boxes/-/cli-boxes-2.2.1.tgz" - integrity sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw== - -cli-truncate@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz" - integrity sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg== - dependencies: - slice-ansi "^3.0.0" - string-width "^4.2.0" - -cliui@^7.0.2: - version "7.0.4" - resolved "https://registry.yarnpkg.com/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" - -clone-response@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz" - integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= - dependencies: - mimic-response "^1.0.0" - -color-convert@^1.9.0: - version "1.9.3" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz" - integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== - dependencies: - color-name "1.1.3" - -color-convert@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== - dependencies: - color-name "~1.1.4" - -color-name@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz" - integrity sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= - -color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz" - integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= - -concat-stream@^1.6.2: - version "1.6.2" - resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz" - integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== - dependencies: - buffer-from "^1.0.0" - inherits "^2.0.3" - readable-stream "^2.2.2" - typedarray "^0.0.6" - -config-chain@^1.1.11: - version "1.1.12" - resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.12.tgz" - integrity sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA== - dependencies: - ini "^1.3.4" - proto-list "~1.2.1" - -configstore@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/configstore/-/configstore-5.0.1.tgz" - integrity sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA== - dependencies: - dot-prop "^5.2.0" - graceful-fs "^4.1.2" - make-dir "^3.0.0" - unique-string "^2.0.0" - write-file-atomic "^3.0.0" - xdg-basedir "^4.0.0" - -copy-dir@1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/copy-dir/-/copy-dir-1.3.0.tgz" - integrity sha512-Q4+qBFnN4bwGwvtXXzbp4P/4iNk0MaiGAzvQ8OiMtlLjkIKjmNN689uVzShSM0908q7GoFHXIPx4zi75ocoaHw== - -core-js@^3.6.5: - version "3.9.1" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.9.1.tgz" - integrity sha512-gSjRvzkxQc1zjM/5paAmL4idJBFzuJoo+jDjF1tStYFMV2ERfD02HhahhCGXUyHxQRG4yFKVSdO6g62eoRMcDg== - -core-util-is@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz" - integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= - -crypto-random-string@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz" - integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== - -css-select@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz" - integrity sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg= - dependencies: - boolbase "~1.0.0" - css-what "2.1" - domutils "1.5.1" - nth-check "~1.0.1" - -css-what@2.1: - version "2.1.3" - resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.3.tgz" - integrity sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg== - -debug@^2.6.9: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - -debug@^4.1.0, debug@^4.1.1, debug@^4.3.0: - version "4.3.1" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.1.tgz" - integrity sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ== - dependencies: - ms "2.1.2" - -decompress-response@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz" - integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M= - dependencies: - mimic-response "^1.0.0" - -deep-extend@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz" - integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== - -defer-to-connect@^1.0.1: - version "1.1.3" - resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz" - integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ== - -define-properties@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz" - integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== - dependencies: - object-keys "^1.0.12" - -detect-node@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.4.tgz" - integrity sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw== - -dmg-builder@22.9.1: - version "22.9.1" - resolved "https://registry.yarnpkg.com/dmg-builder/-/dmg-builder-22.9.1.tgz" - integrity sha512-jc+DAirqmQrNT6KbDHdfEp8D1kD0DBTnsLhwUR3MX+hMBun5bT134LQzpdK0GKvd22GqF8L1Cz/NOgaVjscAXQ== - dependencies: - app-builder-lib "22.9.1" - builder-util "22.9.1" - fs-extra "^9.0.1" - iconv-lite "^0.6.2" - js-yaml "^3.14.0" - sanitize-filename "^1.6.3" - -dom-serializer@0: - version "0.2.2" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz" - integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g== - dependencies: - domelementtype "^2.0.1" - entities "^2.0.0" - -dom-serializer@~0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.1.tgz" - integrity sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA== - dependencies: - domelementtype "^1.3.0" - entities "^1.1.1" - -domelementtype@1, domelementtype@^1.3.0, domelementtype@^1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz" - integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== - -domelementtype@^2.0.1: - version "2.1.0" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.1.0.tgz" - integrity sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w== - -domhandler@^2.3.0: - version "2.4.2" - resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz" - integrity sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA== - dependencies: - domelementtype "1" - -domutils@1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz" - integrity sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8= - dependencies: - dom-serializer "0" - domelementtype "1" - -domutils@^1.5.1: - version "1.7.0" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz" - integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg== - dependencies: - dom-serializer "0" - domelementtype "1" - -dot-prop@^5.2.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz" - integrity sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q== - dependencies: - is-obj "^2.0.0" - -dotenv-expand@^5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-5.1.0.tgz" - integrity sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA== - -dotenv@^8.2.0: - version "8.2.0" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz" - integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw== - -duplexer3@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz" - integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI= - -ejs@^3.1.5: - version "3.1.6" - resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.6.tgz" - integrity sha512-9lt9Zse4hPucPkoP7FHDF0LQAlGyF9JVpnClFLFH3aSSbxmyoqINRpp/9wePWJTUl4KOQwRL72Iw3InHPDkoGw== - dependencies: - jake "^10.6.1" - -electron-builder@22.9.1: - version "22.9.1" - resolved "https://registry.yarnpkg.com/electron-builder/-/electron-builder-22.9.1.tgz" - integrity sha512-GXPt8l5Mxwm1QKYopUM6/Tdh9W3695G6Ax+IFyj5pQ51G4SD5L1uq4/RkPSsOgs3rP7jNSV6g6OfDzdtVufPdA== - dependencies: - "@types/yargs" "^15.0.5" - app-builder-lib "22.9.1" - bluebird-lst "^1.0.9" - builder-util "22.9.1" - builder-util-runtime "8.7.2" - chalk "^4.1.0" - dmg-builder "22.9.1" - fs-extra "^9.0.1" - is-ci "^2.0.0" - lazy-val "^1.0.4" - read-config-file "6.0.0" - sanitize-filename "^1.6.3" - update-notifier "^4.1.1" - yargs "^16.0.3" - -electron-context-menu@2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/electron-context-menu/-/electron-context-menu-2.1.0.tgz" - integrity sha512-xnJS4C24W/h6rw2fbRYMiHCHWzbJeuhPTLJR88lJ+jSnO8VKY0oOolE2AeL/zTTiUYrOXF+3POcfpSoaF0d8Pw== - dependencies: - cli-truncate "^2.0.0" - electron-dl "^3.0.0" - electron-is-dev "^1.0.1" - -electron-dl@^3.0.0: - version "3.2.1" - resolved "https://registry.yarnpkg.com/electron-dl/-/electron-dl-3.2.1.tgz" - integrity sha512-k5DFjocJlXbrjshO1zeWe/Gz7HkGwCgnehHPemiyzN2B/LfLlnbIX7sCj5F+huTwZ2l+nQehTI4IR37xvCn6FQ== - dependencies: - ext-name "^5.0.0" - pupa "^2.0.1" - unused-filename "^2.1.0" - -electron-is-dev@^1.0.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/electron-is-dev/-/electron-is-dev-1.2.0.tgz" - integrity sha512-R1oD5gMBPS7PVU8gJwH6CtT0e6VSoD0+SzSnYpNm+dBkcijgA+K7VAMHDfnRq/lkKPZArpzplTW6jfiMYosdzw== - -electron-publish@22.9.1: - version "22.9.1" - resolved "https://registry.yarnpkg.com/electron-publish/-/electron-publish-22.9.1.tgz" - integrity sha512-ducLjRJLEeU87FaTCWaUyDjCoLXHkawkltP2zqS/n2PyGke54ZIql0tBuUheht4EpR8AhFbVJ11spSn1gy8r6w== - dependencies: - "@types/fs-extra" "^9.0.1" - bluebird-lst "^1.0.9" - builder-util "22.9.1" - builder-util-runtime "8.7.2" - chalk "^4.1.0" - fs-extra "^9.0.1" - lazy-val "^1.0.4" - mime "^2.4.6" - -electron-window-state@^5.0.3: - version "5.0.3" - resolved "https://registry.npmjs.org/electron-window-state/-/electron-window-state-5.0.3.tgz" - integrity sha512-1mNTwCfkolXl3kMf50yW3vE2lZj0y92P/HYWFBrb+v2S/pCka5mdwN3cagKm458A7NjndSwijynXgcLWRodsVg== - dependencies: - jsonfile "^4.0.0" - mkdirp "^0.5.1" - -electron@11.1.1: - version "11.1.1" - resolved "https://registry.yarnpkg.com/electron/-/electron-11.1.1.tgz" - integrity sha512-tlbex3xosJgfileN6BAQRotevPRXB/wQIq48QeQ08tUJJrXwE72c8smsM/hbHx5eDgnbfJ2G3a60PmRjHU2NhA== - dependencies: - "@electron/get" "^1.0.1" - "@types/node" "^12.0.12" - extract-zip "^1.0.3" - -emoji-regex@^7.0.1: - version "7.0.3" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz" - integrity sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA== - -emoji-regex@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz" - integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== - -encodeurl@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz" - integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= - -end-of-stream@^1.1.0: - version "1.4.4" - resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz" - integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q== - dependencies: - once "^1.4.0" - -entities@^1.1.1, entities@~1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz" - integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== - -entities@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz" - integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== - -env-paths@^2.2.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz" - integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== - -es6-error@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz" - integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== - -escalade@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz" - integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== - -escape-goat@^2.0.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-2.1.1.tgz" - integrity sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q== - -escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" - integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= - -escape-string-regexp@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" - integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== - -esprima@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz" - integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== - -ext-list@^2.0.0: - version "2.2.2" - resolved "https://registry.yarnpkg.com/ext-list/-/ext-list-2.2.2.tgz" - integrity sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA== - dependencies: - mime-db "^1.28.0" - -ext-name@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/ext-name/-/ext-name-5.0.0.tgz" - integrity sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ== - dependencies: - ext-list "^2.0.0" - sort-keys-length "^1.0.0" - -extract-zip@^1.0.3: - version "1.7.0" - resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-1.7.0.tgz" - integrity sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA== - dependencies: - concat-stream "^1.6.2" - debug "^2.6.9" - mkdirp "^0.5.4" - yauzl "^2.10.0" - -fast-deep-equal@^3.1.1: - version "3.1.3" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" - integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== - -fast-json-stable-stringify@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz" - integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== - -fd-slicer@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz" - integrity sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= - dependencies: - pend "~1.2.0" - -filelist@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.2.tgz" - integrity sha512-z7O0IS8Plc39rTCq6i6iHxk43duYOn8uFJiWSewIq0Bww1RNybVHSCjahmcC87ZqAm4OTvFzlzeGu3XAzG1ctQ== - dependencies: - minimatch "^3.0.4" - -follow-redirects@^1.10.0: - version "1.13.3" - resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.13.3.tgz" - integrity sha512-DUgl6+HDzB0iEptNQEXLx/KhTmDb8tZUHSeLqpnjpknR70H0nC2t9N73BK6fN4hOvJ84pKlIQVQ4k5FFlBedKA== - -fs-extra@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz" - integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== - dependencies: - graceful-fs "^4.2.0" - jsonfile "^4.0.0" - universalify "^0.1.0" - -fs-extra@^9.0.1: - version "9.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz" - integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== - dependencies: - at-least-node "^1.0.0" - graceful-fs "^4.2.0" - jsonfile "^6.0.1" - universalify "^2.0.0" - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz" - integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= - -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== - -get-caller-file@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz" - integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== - -get-port@5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/get-port/-/get-port-5.1.1.tgz" - integrity sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ== - -get-stream@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz" - integrity sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w== - dependencies: - pump "^3.0.0" - -get-stream@^5.1.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz" - integrity sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA== - dependencies: - pump "^3.0.0" - -glob@^7.1.3: - version "7.1.6" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz" - integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - -global-agent@^2.0.2: - version "2.1.12" - resolved "https://registry.yarnpkg.com/global-agent/-/global-agent-2.1.12.tgz" - integrity sha512-caAljRMS/qcDo69X9BfkgrihGUgGx44Fb4QQToNQjsiWh+YlQ66uqYVAdA8Olqit+5Ng0nkz09je3ZzANMZcjg== - dependencies: - boolean "^3.0.1" - core-js "^3.6.5" - es6-error "^4.1.1" - matcher "^3.0.0" - roarr "^2.15.3" - semver "^7.3.2" - serialize-error "^7.0.1" - -global-dirs@^2.0.1: - version "2.1.0" - resolved "https://registry.yarnpkg.com/global-dirs/-/global-dirs-2.1.0.tgz" - integrity sha512-MG6kdOUh/xBnyo9cJFeIKkLEc1AyFq42QTU4XiX51i2NEdxLxLWXIjEjmqKeSuKR7pAZjTqUVoT2b2huxVLgYQ== - dependencies: - ini "1.3.7" - -global-tunnel-ng@^2.7.1: - version "2.7.1" - resolved "https://registry.yarnpkg.com/global-tunnel-ng/-/global-tunnel-ng-2.7.1.tgz" - integrity sha512-4s+DyciWBV0eK148wqXxcmVAbFVPqtc3sEtUE/GTQfuU80rySLcMhUmHKSHI7/LDj8q0gDYI1lIhRRB7ieRAqg== - dependencies: - encodeurl "^1.0.2" - lodash "^4.17.10" - npm-conf "^1.1.3" - tunnel "^0.0.6" - -globalthis@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/globalthis/-/globalthis-1.0.2.tgz" - integrity sha512-ZQnSFO1la8P7auIOQECnm0sSuoMeaSq0EEdXMBFF2QJO4uNcwbyhSgG3MruWNbFTqCLmxVwGOl7LZ9kASvHdeQ== - dependencies: - define-properties "^1.1.3" - -got@^9.6.0: - version "9.6.0" - resolved "https://registry.yarnpkg.com/got/-/got-9.6.0.tgz" - integrity sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q== - dependencies: - "@sindresorhus/is" "^0.14.0" - "@szmarczak/http-timer" "^1.1.2" - cacheable-request "^6.0.0" - decompress-response "^3.3.0" - duplexer3 "^0.1.4" - get-stream "^4.1.0" - lowercase-keys "^1.0.1" - mimic-response "^1.0.1" - p-cancelable "^1.0.0" - to-readable-stream "^1.0.0" - url-parse-lax "^3.0.0" - -graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0: - version "4.2.6" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz" - integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ== - -has-flag@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz" - integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= - -has-flag@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz" - integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== - -has-yarn@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/has-yarn/-/has-yarn-2.1.0.tgz" - integrity sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw== - -has@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz" - integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== - dependencies: - function-bind "^1.1.1" - -hosted-git-info@^2.1.4: - 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== - -hosted-git-info@^3.0.5: - version "3.0.8" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-3.0.8.tgz" - integrity sha512-aXpmwoOhRBrw6X3j0h5RloK4x1OzsxMPyxqIHyNfSe2pypkVTZFpEiRoSipPEPlMrh0HW/XsjkJ5WgnCirpNUw== - dependencies: - lru-cache "^6.0.0" - -htmlparser2@^3.9.1: - version "3.10.1" - resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz" - integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ== - dependencies: - domelementtype "^1.3.1" - domhandler "^2.3.0" - domutils "^1.5.1" - entities "^1.1.1" - inherits "^2.0.1" - readable-stream "^3.1.1" - -http-cache-semantics@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz" - integrity sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ== - -iconv-lite@^0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.2.tgz" - integrity sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ== - dependencies: - safer-buffer ">= 2.1.2 < 3.0.0" - -import-lazy@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-2.1.0.tgz" - integrity sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM= - -imurmurhash@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz" - integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz" - integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk= - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -ini@1.3.7: - version "1.3.7" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.7.tgz" - integrity sha512-iKpRpXP+CrP2jyrxvg1kMUpXDyRUFDWurxbnVT1vQPx+Wz9uCYsMIqYuSBLV+PAaZG/d7kRLKRFc9oDMsH+mFQ== - -ini@^1.3.4, ini@~1.3.0: - version "1.3.8" - resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz" - integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== - -is-ci@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz" - integrity sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w== - dependencies: - ci-info "^2.0.0" - -is-core-module@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.2.0.tgz" - integrity sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ== - dependencies: - has "^1.0.3" - -is-fullwidth-code-point@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz" - integrity sha1-o7MKXE8ZkYMWeqq5O+764937ZU8= - -is-fullwidth-code-point@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" - integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== - -is-installed-globally@^0.3.1: - version "0.3.2" - resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.3.2.tgz" - integrity sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g== - dependencies: - global-dirs "^2.0.1" - is-path-inside "^3.0.1" - -is-npm@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-4.0.0.tgz" - integrity sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig== - -is-obj@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz" - integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== - -is-path-inside@^3.0.1: - version "3.0.3" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz" - integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== - -is-plain-obj@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-1.1.0.tgz" - integrity sha1-caUMhCnfync8kqOQpKA7OfzVHT4= - -is-typedarray@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz" - integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= - -is-yarn-global@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/is-yarn-global/-/is-yarn-global-0.3.0.tgz" - integrity sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw== - -isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz" - integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= - -isbinaryfile@^4.0.6: - version "4.0.6" - resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-4.0.6.tgz" - integrity sha512-ORrEy+SNVqUhrCaal4hA4fBzhggQQ+BaLntyPOdoEiwlKZW9BZiJXjg3RMiruE4tPEI3pyVPpySHQF/dKWperg== - -jake@^10.6.1: - version "10.8.2" - resolved "https://registry.yarnpkg.com/jake/-/jake-10.8.2.tgz" - integrity sha512-eLpKyrfG3mzvGE2Du8VoPbeSkRry093+tyNjdYaBbJS9v17knImYGNXQCUV0gLxQtF82m3E8iRb/wdSQZLoq7A== - dependencies: - async "0.9.x" - chalk "^2.4.2" - filelist "^1.0.1" - minimatch "^3.0.4" - -js-yaml@^3.13.1, js-yaml@^3.14.0: - version "3.14.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz" - integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - -json-buffer@3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz" - integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg= - -json-schema-traverse@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz" - integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== - -json-stringify-safe@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz" - integrity sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= - -json5@^2.1.2: - version "2.2.0" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.0.tgz" - integrity sha512-f+8cldu7X/y7RAJurMEJmdoKXGB/X550w2Nr3tTbezL6RwEE/iMcm+tZnXeoZtKuOq6ft8+CqzEkrIgx1fPoQA== - dependencies: - minimist "^1.2.5" - -jsonfile@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz" - integrity sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= - optionalDependencies: - graceful-fs "^4.1.6" - -jsonfile@^6.0.1: - version "6.1.0" - resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz" - integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== - dependencies: - universalify "^2.0.0" - optionalDependencies: - graceful-fs "^4.1.6" - -keyv@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.1.0.tgz" - integrity sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA== - dependencies: - json-buffer "3.0.0" - -latest-version@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-5.1.0.tgz" - integrity sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA== - dependencies: - package-json "^6.3.0" - -lazy-val@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/lazy-val/-/lazy-val-1.0.4.tgz" - integrity sha512-u93kb2fPbIrfzBuLjZE+w+fJbUUMhNDXxNmMfaqNgpfQf1CO5ZSe2LfsnBqVAk7i/2NF48OSoRj+Xe2VT+lE8Q== - -lodash@^4.15.0, lodash@^4.17.10: - version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== - -lowercase-keys@^1.0.0, lowercase-keys@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz" - integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA== - -lowercase-keys@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz" - integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== - -lru-cache@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz" - integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== - dependencies: - yallist "^4.0.0" - -make-dir@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz" - integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== - dependencies: - semver "^6.0.0" - -matcher@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/matcher/-/matcher-3.0.0.tgz" - integrity sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng== - dependencies: - escape-string-regexp "^4.0.0" - -mime-db@^1.28.0: - version "1.46.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.46.0.tgz" - integrity sha512-svXaP8UQRZ5K7or+ZmfNhg2xX3yKDMUzqadsSqi4NCH/KomcH75MAMYAGVlvXn4+b/xOPhS3I2uHKRUzvjY7BQ== - -mime@^2.4.6: - version "2.5.2" - resolved "https://registry.yarnpkg.com/mime/-/mime-2.5.2.tgz" - integrity sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg== - -mimic-response@^1.0.0, mimic-response@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz" - integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ== - -minimatch@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz" - integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== - dependencies: - brace-expansion "^1.1.7" - -minimist@^1.2.0, minimist@^1.2.5: - version "1.2.5" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz" - integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== - -mkdirp@^0.5.1, mkdirp@^0.5.4: - version "0.5.5" - resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz" - integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ== - dependencies: - minimist "^1.2.5" - -modify-filename@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/modify-filename/-/modify-filename-1.1.0.tgz" - integrity sha1-mi3sg4Bvuy2XXyK+7IWcoms5OqE= - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz" - integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= - -ms@2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - -normalize-package-data@^2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz" - integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== - dependencies: - hosted-git-info "^2.1.4" - resolve "^1.10.0" - semver "2 || 3 || 4 || 5" - validate-npm-package-license "^3.0.1" - -normalize-url@^4.1.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-4.5.0.tgz" - integrity sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ== - -npm-conf@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/npm-conf/-/npm-conf-1.1.3.tgz" - integrity sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw== - dependencies: - config-chain "^1.1.11" - pify "^3.0.0" - -nth-check@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.2.tgz" - integrity sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg== - dependencies: - boolbase "~1.0.0" - -object-keys@^1.0.12: - version "1.1.1" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz" - integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== - -once@^1.3.0, once@^1.3.1, once@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz" - integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= - dependencies: - wrappy "1" - -p-cancelable@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-1.1.0.tgz" - integrity sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw== - -package-json@^6.3.0: - version "6.5.0" - resolved "https://registry.yarnpkg.com/package-json/-/package-json-6.5.0.tgz" - integrity sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ== - dependencies: - got "^9.6.0" - registry-auth-token "^4.0.0" - registry-url "^5.0.0" - semver "^6.2.0" - -parse5@^3.0.1: - version "3.0.3" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-3.0.3.tgz" - integrity sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA== - dependencies: - "@types/node" "*" - -path-exists@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz" - integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== - -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= - -path-parse@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz" - integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== - -pend@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz" - integrity sha1-elfrVQpng/kRUzH89GY9XI4AelA= - -pify@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-3.0.0.tgz" - integrity sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY= - -prepend-http@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz" - integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc= - -process-nextick-args@~2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz" - integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== - -progress@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz" - integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== - -proto-list@~1.2.1: - version "1.2.4" - resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz" - integrity sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk= - -pump@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz" - integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - -punycode@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== - -pupa@^2.0.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/pupa/-/pupa-2.1.1.tgz" - integrity sha512-l1jNAspIBSFqbT+y+5FosojNpVpF94nlI+wDUpqP9enwOTfHx9f0gh5nB96vl+6yTpsJsypeNrwfzPrKuHB41A== - dependencies: - escape-goat "^2.0.0" - -rc@^1.2.8: - version "1.2.8" - resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.8.tgz" - integrity sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== - dependencies: - deep-extend "^0.6.0" - ini "~1.3.0" - minimist "^1.2.0" - strip-json-comments "~2.0.1" - -read-config-file@6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/read-config-file/-/read-config-file-6.0.0.tgz" - integrity sha512-PHjROSdpceKUmqS06wqwP92VrM46PZSTubmNIMJ5DrMwg1OgenSTSEHIkCa6TiOJ+y/J0xnG1fFwG3M+Oi1aNA== - dependencies: - dotenv "^8.2.0" - dotenv-expand "^5.1.0" - js-yaml "^3.13.1" - json5 "^2.1.2" - lazy-val "^1.0.4" - -readable-stream@^2.2.2: - version "2.3.7" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz" - integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~2.0.0" - safe-buffer "~5.1.1" - string_decoder "~1.1.1" - util-deprecate "~1.0.1" - -readable-stream@^3.1.1: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz" - integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== - dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" - -registry-auth-token@^4.0.0: - version "4.2.1" - resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-4.2.1.tgz" - integrity sha512-6gkSb4U6aWJB4SF2ZvLb76yCBjcvufXBqvvEx1HbmKPkutswjW1xNVRY0+daljIYRbogN7O0etYSlbiaEQyMyw== - dependencies: - rc "^1.2.8" - -registry-url@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/registry-url/-/registry-url-5.1.0.tgz" - integrity sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw== - dependencies: - rc "^1.2.8" - -require-directory@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz" - integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I= - -resolve@^1.10.0: - version "1.20.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz" - integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== - dependencies: - is-core-module "^2.2.0" - path-parse "^1.0.6" - -responselike@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz" - integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec= - dependencies: - lowercase-keys "^1.0.0" - -rimraf@3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz" - integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== - dependencies: - glob "^7.1.3" - -roarr@^2.15.3: - version "2.15.4" - resolved "https://registry.yarnpkg.com/roarr/-/roarr-2.15.4.tgz" - integrity sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A== - dependencies: - boolean "^3.0.1" - detect-node "^2.0.4" - globalthis "^1.0.1" - json-stringify-safe "^5.0.1" - semver-compare "^1.0.0" - sprintf-js "^1.1.2" - -safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -safe-buffer@~5.2.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - -"safer-buffer@>= 2.1.2 < 3.0.0": - version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== - -sanitize-filename@^1.6.3: - version "1.6.3" - resolved "https://registry.yarnpkg.com/sanitize-filename/-/sanitize-filename-1.6.3.tgz" - integrity sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg== - dependencies: - truncate-utf8-bytes "^1.0.0" - -sax@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz" - integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== - -semver-compare@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/semver-compare/-/semver-compare-1.0.0.tgz" - integrity sha1-De4hahyUGrN+nvsXiPavxf9VN/w= - -semver-diff@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/semver-diff/-/semver-diff-3.1.1.tgz" - integrity sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg== - dependencies: - semver "^6.3.0" - -"semver@2 || 3 || 4 || 5": - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== - -semver@^6.0.0, semver@^6.2.0, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== - -semver@^7.3.2: - version "7.3.4" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.4.tgz" - integrity sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw== - dependencies: - lru-cache "^6.0.0" - -serialize-error@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-7.0.1.tgz" - integrity sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw== - dependencies: - type-fest "^0.13.1" - -signal-exit@^3.0.2: - version "3.0.3" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz" - integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA== - -slice-ansi@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz" - integrity sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ== - dependencies: - ansi-styles "^4.0.0" - astral-regex "^2.0.0" - is-fullwidth-code-point "^3.0.0" - -sort-keys-length@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/sort-keys-length/-/sort-keys-length-1.0.1.tgz" - integrity sha1-nLb09OnkgVWmqgZx7dM2/xR5oYg= - dependencies: - sort-keys "^1.0.0" - -sort-keys@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz" - integrity sha1-RBttTTRnmPG05J6JIK37oOVD+a0= - dependencies: - is-plain-obj "^1.0.0" - -source-map-support@^0.5.19: - version "0.5.19" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.19.tgz" - integrity sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - -source-map@^0.6.0: - version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - -spdx-correct@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz" - integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== - dependencies: - spdx-expression-parse "^3.0.0" - spdx-license-ids "^3.0.0" - -spdx-exceptions@^2.1.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz" - integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== - -spdx-expression-parse@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz" - integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== - dependencies: - spdx-exceptions "^2.1.0" - spdx-license-ids "^3.0.0" - -spdx-license-ids@^3.0.0: - version "3.0.7" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz" - integrity sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ== - -sprintf-js@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.2.tgz" - integrity sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug== - -sprintf-js@~1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz" - integrity sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw= - -stat-mode@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/stat-mode/-/stat-mode-1.0.0.tgz" - integrity sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg== - -string-width@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz" - integrity sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w== - dependencies: - emoji-regex "^7.0.1" - is-fullwidth-code-point "^2.0.0" - strip-ansi "^5.1.0" - -string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0: - version "4.2.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.2.tgz" - integrity sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.0" - -string_decoder@^1.1.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== - dependencies: - safe-buffer "~5.2.0" - -string_decoder@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz" - integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg== - dependencies: - safe-buffer "~5.1.0" - -strip-ansi@^5.1.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz" - integrity sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA== - dependencies: - ansi-regex "^4.1.0" - -strip-ansi@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz" - integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== - dependencies: - ansi-regex "^5.0.0" - -strip-json-comments@~2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz" - integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= - -sumchecker@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/sumchecker/-/sumchecker-3.0.1.tgz" - integrity sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg== - dependencies: - debug "^4.1.0" - -supports-color@^5.3.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz" - integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== - dependencies: - has-flag "^3.0.0" - -supports-color@^7.1.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz" - integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== - dependencies: - has-flag "^4.0.0" - -temp-file@^3.3.7: - version "3.3.7" - resolved "https://registry.yarnpkg.com/temp-file/-/temp-file-3.3.7.tgz" - integrity sha512-9tBJKt7GZAQt/Rg0QzVWA8Am8c1EFl+CAv04/aBVqlx5oyfQ508sFIABshQ0xbZu6mBrFLWIUXO/bbLYghW70g== - dependencies: - async-exit-hook "^2.0.1" - fs-extra "^8.1.0" - -term-size@^2.1.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/term-size/-/term-size-2.2.1.tgz" - integrity sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg== - -to-readable-stream@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/to-readable-stream/-/to-readable-stream-1.0.0.tgz" - integrity sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q== - -truncate-utf8-bytes@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz" - integrity sha1-QFkjkJWS1W94pYGENLC3hInKXys= - dependencies: - utf8-byte-length "^1.0.1" - -tunnel@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/tunnel/-/tunnel-0.0.6.tgz" - integrity sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg== - -type-fest@^0.13.1: - version "0.13.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.13.1.tgz" - integrity sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg== - -type-fest@^0.8.1: - version "0.8.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz" - integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== - -typedarray-to-buffer@^3.1.5: - version "3.1.5" - resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz" - integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== - dependencies: - is-typedarray "^1.0.0" - -typedarray@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz" - integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= - -unique-string@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz" - integrity sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg== - dependencies: - crypto-random-string "^2.0.0" - -universalify@^0.1.0: - version "0.1.2" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz" - integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== - -universalify@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz" - integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== - -unused-filename@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/unused-filename/-/unused-filename-2.1.0.tgz" - integrity sha512-BMiNwJbuWmqCpAM1FqxCTD7lXF97AvfQC8Kr/DIeA6VtvhJaMDupZ82+inbjl5yVP44PcxOuCSxye1QMS0wZyg== - dependencies: - modify-filename "^1.1.0" - path-exists "^4.0.0" - -update-notifier@^4.1.1: - version "4.1.3" - resolved "https://registry.yarnpkg.com/update-notifier/-/update-notifier-4.1.3.tgz" - integrity sha512-Yld6Z0RyCYGB6ckIjffGOSOmHXj1gMeE7aROz4MG+XMkmixBX4jUngrGXNYz7wPKBmtoD4MnBa2Anu7RSKht/A== - dependencies: - boxen "^4.2.0" - chalk "^3.0.0" - configstore "^5.0.1" - has-yarn "^2.1.0" - import-lazy "^2.1.0" - is-ci "^2.0.0" - is-installed-globally "^0.3.1" - is-npm "^4.0.0" - is-yarn-global "^0.3.0" - latest-version "^5.0.0" - pupa "^2.0.1" - semver-diff "^3.1.1" - xdg-basedir "^4.0.0" - -uri-js@^4.2.2: - version "4.4.1" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz" - integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== - dependencies: - punycode "^2.1.0" - -url-parse-lax@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz" - integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww= - dependencies: - prepend-http "^2.0.0" - -utf8-byte-length@^1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/utf8-byte-length/-/utf8-byte-length-1.0.4.tgz" - integrity sha1-9F8VDExm7uloGGUFq5P8u4rWv2E= - -util-deprecate@^1.0.1, util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz" - integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= - -v8-compile-cache@2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz" - integrity sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ== - -validate-npm-package-license@^3.0.1: - version "3.0.4" - resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz" - integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== - dependencies: - spdx-correct "^3.0.0" - spdx-expression-parse "^3.0.0" - -widest-line@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/widest-line/-/widest-line-3.1.0.tgz" - integrity sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg== - dependencies: - string-width "^4.0.0" - -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/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" - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz" - integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= - -write-file-atomic@^3.0.0: - version "3.0.3" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-3.0.3.tgz" - integrity sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q== - dependencies: - imurmurhash "^0.1.4" - is-typedarray "^1.0.0" - signal-exit "^3.0.2" - typedarray-to-buffer "^3.1.5" - -xdg-basedir@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-4.0.0.tgz" - integrity sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q== - -y18n@^5.0.5: - version "5.0.5" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.5.tgz" - integrity sha512-hsRUr4FFrvhhRH12wOdfs38Gy7k2FFzB9qgN9v3aLykRq0dRcdcpz5C9FxdS2NuhOrI/628b/KSTJ3rwHysYSg== - -yallist@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz" - integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== - -yargs-parser@^20.2.2: - version "20.2.7" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.7.tgz" - integrity sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw== - -yargs@^16.0.3: - version "16.2.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz" - integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== - 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" - -yauzl@^2.10.0: - version "2.10.0" - resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz" - integrity sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= - dependencies: - buffer-crc32 "~0.2.3" - fd-slicer "~1.1.0" diff --git a/vripper-gui/.gitignore b/vripper-gui/.gitignore new file mode 100644 index 00000000..549e00a2 --- /dev/null +++ b/vripper-gui/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/vripper-gui/.mvn/wrapper/maven-wrapper.jar b/vripper-gui/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..c1dd12f17644411d6e840bd5a10c6ecda0175f18 GIT binary patch literal 58727 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%0ANsA|A3-jj|r`KAo%oTajX3>^E` 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 YM{}PByk10KD_nuQ4E7Du?}+~TKh4V)`~Uy| literal 0 HcmV?d00001 diff --git a/vripper-gui/.mvn/wrapper/maven-wrapper.properties b/vripper-gui/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 00000000..b74bf7fc --- /dev/null +++ b/vripper-gui/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,2 @@ +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.6/apache-maven-3.8.6-bin.zip +wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar diff --git a/vripper-gui/mvnw b/vripper-gui/mvnw new file mode 100644 index 00000000..8a8fb228 --- /dev/null +++ b/vripper-gui/mvnw @@ -0,0 +1,316 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /usr/local/etc/mavenrc ] ; then + . /usr/local/etc/mavenrc + fi + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + 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 + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Mingw, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + 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 + else + JAVACMD="`\\unset -f command; \\command -v java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +########################################################################################## +# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +# This allows using the maven wrapper in projects that prohibit checking in binary data. +########################################################################################## +if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found .mvn/wrapper/maven-wrapper.jar" + fi +else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." + fi + if [ -n "$MVNW_REPOURL" ]; then + jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + else + jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + fi + while IFS="=" read key value; do + case "$key" in (wrapperUrl) jarUrl="$value"; break ;; + esac + done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" + if [ "$MVNW_VERBOSE" = true ]; then + echo "Downloading from: $jarUrl" + fi + wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" + if $cygwin; then + wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` + fi + + if command -v wget > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found wget ... using wget" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + else + wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" + fi + elif command -v curl > /dev/null; then + if [ "$MVNW_VERBOSE" = true ]; then + echo "Found curl ... using curl" + fi + if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then + curl -o "$wrapperJarPath" "$jarUrl" -f + else + curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f + fi + + else + if [ "$MVNW_VERBOSE" = true ]; then + echo "Falling back to using Java to download" + fi + javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" + # For Cygwin, switch paths to Windows format before running javac + if $cygwin; then + javaClass=`cygpath --path --windows "$javaClass"` + fi + if [ -e "$javaClass" ]; then + if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Compiling MavenWrapperDownloader.java ..." + fi + # Compiling the Java class + ("$JAVA_HOME/bin/javac" "$javaClass") + fi + if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then + # Running the downloader + if [ "$MVNW_VERBOSE" = true ]; then + echo " - Running MavenWrapperDownloader.java ..." + fi + ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") + fi + fi + fi +fi +########################################################################################## +# End of extension +########################################################################################## + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +if [ "$MVNW_VERBOSE" = true ]; then + echo $MAVEN_PROJECTBASEDIR +fi +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +# Provide a "standardized" way to retrieve the CLI args that will +# work with both Windows and non-Windows executions. +MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" +export MAVEN_CMD_LINE_ARGS + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + $MAVEN_DEBUG_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" \ + "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/vripper-gui/mvnw.cmd b/vripper-gui/mvnw.cmd new file mode 100644 index 00000000..1d8ab018 --- /dev/null +++ b/vripper-gui/mvnw.cmd @@ -0,0 +1,188 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. 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, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM set title of command window +title %0 +@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* +if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + +FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( + IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B +) + +@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central +@REM This allows using the maven wrapper in projects that prohibit checking in binary data. +if exist %WRAPPER_JAR% ( + if "%MVNW_VERBOSE%" == "true" ( + echo Found %WRAPPER_JAR% + ) +) else ( + if not "%MVNW_REPOURL%" == "" ( + SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" + ) + if "%MVNW_VERBOSE%" == "true" ( + echo Couldn't find %WRAPPER_JAR%, downloading it ... + echo Downloading from: %DOWNLOAD_URL% + ) + + powershell -Command "&{"^ + "$webclient = new-object System.Net.WebClient;"^ + "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ + "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ + "}"^ + "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ + "}" + if "%MVNW_VERBOSE%" == "true" ( + echo Finished downloading %WRAPPER_JAR% + ) +) +@REM End of extension + +@REM Provide a "standardized" way to retrieve the CLI args that will +@REM work with both Windows and non-Windows executions. +set MAVEN_CMD_LINE_ARGS=%* + +%MAVEN_JAVA_EXE% ^ + %JVM_CONFIG_MAVEN_PROPS% ^ + %MAVEN_OPTS% ^ + %MAVEN_DEBUG_OPTS% ^ + -classpath %WRAPPER_JAR% ^ + "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ + %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" +if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%"=="on" pause + +if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% + +cmd /C exit /B %ERROR_CODE% diff --git a/vripper-gui/pom.xml b/vripper-gui/pom.xml new file mode 100644 index 00000000..1f518dec --- /dev/null +++ b/vripper-gui/pom.xml @@ -0,0 +1,154 @@ + + + 4.0.0 + + spring-boot-starter-parent + org.springframework.boot + + 3.0.6 + + me.mnlr.vripper + vripper-gui + 4.0.0 + vripper-gui + vripper-gui + + + spring-boot-starter + org.springframework.boot + + + kotlin-reflect + org.jetbrains.kotlin + + + kotlin-stdlib + org.jetbrains.kotlin + + + javafx-base + org.openjfx + ${javafx.version} + + + javafx-base + win + org.openjfx + ${javafx.version} + + + javafx-base + linux + org.openjfx + ${javafx.version} + + + javafx-base + mac + org.openjfx + ${javafx.version} + + + javafx-controls + org.openjfx + ${javafx.version} + + + javafx-controls + win + org.openjfx + ${javafx.version} + + + javafx-controls + linux + org.openjfx + ${javafx.version} + + + javafx-controls + mac + org.openjfx + ${javafx.version} + + + javafx-graphics + win + org.openjfx + ${javafx.version} + + + javafx-graphics + linux + org.openjfx + ${javafx.version} + + + javafx-graphics + mac + org.openjfx + ${javafx.version} + + + tornadofx + no.tornado + 1.7.20 + + + vripper-core + me.mnlr.vripper + ${project.version} + + + + spring-boot-starter-test + org.springframework.boot + test + + + + 17 + 1.8.21 + 20.0.1 + false + + + ${project.basedir}/src/main/kotlin + ${project.basedir}/src/test/kotlin + + + org.springframework.boot + spring-boot-maven-plugin + + + org.jetbrains.kotlin + kotlin-maven-plugin + + + -Xjsr305=strict + + + spring + + + + + kotlin-maven-allopen + org.jetbrains.kotlin + ${kotlin.version} + + + + + + + + + github + GitHub death-claw Apache Maven Packages + https://maven.pkg.github.com/death-claw/vripper-project + + + diff --git a/vripper-gui/src/main/kotlin/me/mnlr/vripper/VripperGuiApplication.kt b/vripper-gui/src/main/kotlin/me/mnlr/vripper/VripperGuiApplication.kt new file mode 100644 index 00000000..7271c9cf --- /dev/null +++ b/vripper-gui/src/main/kotlin/me/mnlr/vripper/VripperGuiApplication.kt @@ -0,0 +1,76 @@ +package me.mnlr.vripper + +import javafx.application.Application +import javafx.scene.image.Image +import javafx.stage.Stage +import javafx.stage.WindowEvent +import org.springframework.boot.SpringApplication +import org.springframework.boot.autoconfigure.SpringBootApplication +import org.springframework.context.ConfigurableApplicationContext +import me.mnlr.vripper.event.ApplicationInitialized +import me.mnlr.vripper.gui.Styles +import me.mnlr.vripper.view.LoadingView +import tornadofx.* +import kotlin.reflect.KClass +import kotlin.system.exitProcess + +@SpringBootApplication +class VripperGuiApplication : App( + LoadingView::class, Styles::class +) { //The application class must be a TornadoFX application and it must have the main view + + private lateinit var context: ConfigurableApplicationContext //We are going to set application context here + private var initialized = false + override fun start(stage: Stage) { + with(stage) { + width = 1200.0 + height = 600.0 + icons.addAll( + listOf( + Image("icons/16x16.png"), + Image("icons/32x32.png"), + Image("icons/48x48.png"), + Image("icons/64x64.png"), + Image("icons/128x128.png"), + Image("icons/256x256.png"), + Image("icons/512x512.png"), + Image("icons/1024x1024.png") + ) + ) + } + stage.addEventFilter(WindowEvent.WINDOW_SHOWN) { + val contextInitThread = Thread { + context = + SpringApplication.run(this.javaClass) //We start the application context and let Spring Boot to initilaize itself + context.autowireCapableBeanFactory.autowireBean(this) //We ask the context to inject all needed dependencies into the current instence (if needed) + + FX.dicontainer = object : + DIContainer { // Here we have to implement an interface for TornadoFX DI support + override fun getInstance(type: KClass): T = + context.getBean(type.java) // We find dependencies directly in Spring's application context + + override fun getInstance(type: KClass, name: String): T = + context.getBean(name, type.java) + } + fire(ApplicationInitialized) + initialized = true + } + contextInitThread.apply { + this.name = "Spring init Thread" + }.start() + } + super.start(stage) + } + + override fun stop() { // On stop, we have to stop spring as well + super.stop() + if (initialized) { + context.close() + } + exitProcess(0) + } +} + +fun main(args: Array) { + Application.launch(me.mnlr.vripper.VripperGuiApplication::class.java, *args) +} diff --git a/vripper-gui/src/main/kotlin/me/mnlr/vripper/controller/GlobalStateController.kt b/vripper-gui/src/main/kotlin/me/mnlr/vripper/controller/GlobalStateController.kt new file mode 100644 index 00000000..1c9c3bd4 --- /dev/null +++ b/vripper-gui/src/main/kotlin/me/mnlr/vripper/controller/GlobalStateController.kt @@ -0,0 +1,62 @@ +package me.mnlr.vripper.controller + +import me.mnlr.vripper.event.Event +import me.mnlr.vripper.event.EventBus +import me.mnlr.vripper.formatSI +import me.mnlr.vripper.model.DownloadSpeed +import me.mnlr.vripper.model.GlobalState +import me.mnlr.vripper.model.GlobalStateModel +import me.mnlr.vripper.services.GlobalStateService +import tornadofx.* + +class GlobalStateController : Controller() { + + private val globalStateService: GlobalStateService by di() + private val eventBus: EventBus by di() + + var globalState: GlobalStateModel = GlobalStateModel(0, 0, 0, "", "") + + init { + val currentState = globalStateService.get() + globalState.apply { + running = currentState.running + remaining = currentState.remaining + error = currentState.error + loggedUser = currentState.loggedUser + downloadSpeed = currentState.downloadSpeed + } + + eventBus.flux().subscribe { + if (it!!.kind == Event.Kind.DOWNLOAD_STATUS + ) { + val newState = it.data as GlobalState + globalState.apply { + running = newState.running + remaining = newState.remaining + error = newState.error + } + } + } + + eventBus.flux().subscribe { + if (it!!.kind == Event.Kind.VG_USER + ) { + val user = it.data as String + globalState.apply { + loggedUser = user + } + } + } + + eventBus.flux().subscribe { + if (it!!.kind == Event.Kind.BYTES_PER_SECOND + ) { + val speed = + DownloadSpeed((it.data as Long).formatSI()) + globalState.apply { + downloadSpeed = speed.speed + } + } + } + } +} \ No newline at end of file diff --git a/vripper-gui/src/main/kotlin/me/mnlr/vripper/controller/ImageController.kt b/vripper-gui/src/main/kotlin/me/mnlr/vripper/controller/ImageController.kt new file mode 100644 index 00000000..e06dc065 --- /dev/null +++ b/vripper-gui/src/main/kotlin/me/mnlr/vripper/controller/ImageController.kt @@ -0,0 +1,31 @@ +package me.mnlr.vripper.controller + +import me.mnlr.vripper.entities.ImageDownloadState +import me.mnlr.vripper.model.ImageModel +import me.mnlr.vripper.repositories.ImageRepository +import tornadofx.Controller +import java.util.* + +class ImageController : Controller() { + + private val imageRepository: ImageRepository by di() + + fun findImages(postId: String): List { + return imageRepository.findByPostId(postId).map(::mapper) + } + + fun findImageById(id: Long): Optional { + return imageRepository.findById(id).map(::mapper) + } + + private fun mapper(it: ImageDownloadState): ImageModel { + return ImageModel( + it.id!!, + it.index + 1, + it.url, + if (it.current == 0L && it.total == 0L) 0.0 else (it.current.toDouble() / it.total), + it.status.stringValue, + it.postId + ) + } +} \ No newline at end of file diff --git a/vripper-gui/src/main/kotlin/me/mnlr/vripper/controller/LogController.kt b/vripper-gui/src/main/kotlin/me/mnlr/vripper/controller/LogController.kt new file mode 100644 index 00000000..39343130 --- /dev/null +++ b/vripper-gui/src/main/kotlin/me/mnlr/vripper/controller/LogController.kt @@ -0,0 +1,37 @@ +package me.mnlr.vripper.controller + +import me.mnlr.vripper.AppEndpointService +import me.mnlr.vripper.entities.LogEvent +import me.mnlr.vripper.model.LogModel +import me.mnlr.vripper.repositories.LogEventRepository +import tornadofx.Controller +import java.time.format.DateTimeFormatter +import java.util.* + +class LogController : Controller() { + private val logEventRepository: LogEventRepository by di() + private val appEndpointService: AppEndpointService by di() + private val dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss") + + fun findAll(): List { + return logEventRepository.findAll().map(::mapper) + } + + fun clear() { + appEndpointService.logClear() + } + + fun find(id: Long): Optional { + return logEventRepository.findById(id).map(::mapper) + } + + private fun mapper(it: LogEvent): LogModel { + return LogModel( + it.id!!, + it.type.stringValue, + it.status.stringValue, + it.time.format(dateTimeFormatter), + it.message + ) + } +} \ No newline at end of file diff --git a/vripper-gui/src/main/kotlin/me/mnlr/vripper/controller/PostController.kt b/vripper-gui/src/main/kotlin/me/mnlr/vripper/controller/PostController.kt new file mode 100644 index 00000000..44d749f0 --- /dev/null +++ b/vripper-gui/src/main/kotlin/me/mnlr/vripper/controller/PostController.kt @@ -0,0 +1,69 @@ +package me.mnlr.vripper.controller + +import me.mnlr.vripper.AppEndpointService +import me.mnlr.vripper.entities.PostDownloadState +import me.mnlr.vripper.model.PostModel +import me.mnlr.vripper.repositories.PostDownloadStateRepository +import tornadofx.Controller +import java.time.format.DateTimeFormatter +import java.util.* + +class PostController : Controller() { + + private val postDownloadStateRepository: PostDownloadStateRepository by di() + private val appEndpointService: AppEndpointService by di() + private val dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss") + + fun scan(postLinks: String) { + appEndpointService.scanLinks(postLinks) + } + + fun start(postIdList: List) { + appEndpointService.restartAll(postIdList) + } + + fun startAll() { + appEndpointService.restartAll() + } + + fun delete(postIdList: List) { + appEndpointService.remove(postIdList) + } + + fun stop(postIdList: List) { + appEndpointService.stopAll(postIdList) + + } + + fun clearPosts(): List { + return appEndpointService.clearCompleted() + } + + fun stopAll() { + appEndpointService.stopAll(null) + } + + fun findAllPosts(): List { + return postDownloadStateRepository.findAll().map(::mapper) + } + + fun findById(id: Long): Optional { + return postDownloadStateRepository.findById(id).map(::mapper) + } + + private fun mapper(it: PostDownloadState): PostModel { + return PostModel( + it.postId, + it.postTitle, + if (it.done == 0 && it.total == 0) 0.0 else (it.done.toDouble() / it.total), + it.status.stringValue, + it.url, + it.done, + it.total, + it.hosts.joinToString(separator = "||"), + it.addedOn.format(dateTimeFormatter), + it.rank + 1, + it.downloadDirectory + ) + } +} \ No newline at end of file diff --git a/vripper-gui/src/main/kotlin/me/mnlr/vripper/controller/SettingsController.kt b/vripper-gui/src/main/kotlin/me/mnlr/vripper/controller/SettingsController.kt new file mode 100644 index 00000000..c3fb9295 --- /dev/null +++ b/vripper-gui/src/main/kotlin/me/mnlr/vripper/controller/SettingsController.kt @@ -0,0 +1,69 @@ +package me.mnlr.vripper.controller + +import javafx.scene.control.Alert +import me.mnlr.vripper.exception.ValidationException +import me.mnlr.vripper.model.ConnectionSettings +import me.mnlr.vripper.model.DownloadSettings +import me.mnlr.vripper.model.settings.DownloadSettingsModel +import me.mnlr.vripper.model.Settings +import me.mnlr.vripper.model.ViperSettings +import me.mnlr.vripper.model.settings.ConnectionSettingsModel +import me.mnlr.vripper.model.settings.ViperSettingsModel +import me.mnlr.vripper.services.SettingsService +import tornadofx.Controller +import tornadofx.alert +import tornadofx.warning + +class SettingsController : Controller() { + + private val settingsService: SettingsService by di() + + fun findDownloadSettings(): DownloadSettings { + return settingsService.settings.downloadSettings + } + + fun findConnectionSettings(): ConnectionSettings { + return settingsService.settings.connectionSettings + } + + fun findViperGirlsSettings(): ViperSettings { + return settingsService.settings.viperSettings + } + + fun saveNewSettings( + downloadSettingsModel: DownloadSettingsModel, + connectionSettingsModel: ConnectionSettingsModel, + viperSettingsModel: ViperSettingsModel + ) { + settingsService.newSettings(Settings().apply { + downloadSettings = DownloadSettings( + downloadSettingsModel.downloadPath, + downloadSettingsModel.autoStart, + downloadSettingsModel.autoQueueThreshold, + downloadSettingsModel.forceOrder, + downloadSettingsModel.forumSubfolder, + downloadSettingsModel.threadSubLocation, + downloadSettingsModel.clearCompleted, + downloadSettingsModel.appendPostId + ) + connectionSettings = ConnectionSettings( + connectionSettingsModel.maxThreads, + connectionSettingsModel.maxTotalThreads, + connectionSettingsModel.timeout, + connectionSettingsModel.maxAttempts, + ) + viperSettings = ViperSettings( + viperSettingsModel.login, + viperSettingsModel.username, + viperSettingsModel.password, + viperSettingsModel.thanks, + viperSettingsModel.host, + ) + }) + + } + + fun getProxies(): List { + return settingsService.getProxies() + } +} \ No newline at end of file diff --git a/vripper-gui/src/main/kotlin/me/mnlr/vripper/controller/ThreadController.kt b/vripper-gui/src/main/kotlin/me/mnlr/vripper/controller/ThreadController.kt new file mode 100644 index 00000000..396f996a --- /dev/null +++ b/vripper-gui/src/main/kotlin/me/mnlr/vripper/controller/ThreadController.kt @@ -0,0 +1,61 @@ +package me.mnlr.vripper.controller + +import me.mnlr.vripper.AppEndpointService +import me.mnlr.vripper.entities.Thread +import me.mnlr.vripper.model.ThreadModel +import me.mnlr.vripper.model.ThreadSelectionModel +import me.mnlr.vripper.repositories.ThreadRepository +import tornadofx.* +import java.util.* + +class ThreadController : Controller() { + private val threadRepository: ThreadRepository by di() + + private val appEndpointService: AppEndpointService by di() + + fun findAll(): List { + return threadRepository.findAll().map(::threadModelMapper) + } + + fun find(id: Long): Optional { + return threadRepository.findById(id).map(::threadModelMapper) + } + + private fun threadModelMapper(it: Thread): ThreadModel { + return ThreadModel( + it.link, + it.total, + it.threadId + ) + } + + fun delete(threadIdList: List) { + appEndpointService.threadRemove(threadIdList) + } + + fun clearAll() { + appEndpointService.threadClear() + } + + fun grab(threadId: String): List { + return appEndpointService.grab(threadId).map { + ThreadSelectionModel( + it.number, + it.title, + it.url, + it.hosts, + it.postId, + it.threadId + ) + } + } + + fun download(selectedItems: List) { + appEndpointService.download(selectedItems.map { + Pair( + it.threadId, + it.postId + ) + }) + } +} \ No newline at end of file diff --git a/vripper-gui/src/main/kotlin/me/mnlr/vripper/event/ApplicationInitialized.kt b/vripper-gui/src/main/kotlin/me/mnlr/vripper/event/ApplicationInitialized.kt new file mode 100644 index 00000000..a9bd49f1 --- /dev/null +++ b/vripper-gui/src/main/kotlin/me/mnlr/vripper/event/ApplicationInitialized.kt @@ -0,0 +1,6 @@ +package me.mnlr.vripper.event + +import tornadofx.* +import tornadofx.EventBus + +object ApplicationInitialized : FXEvent(EventBus.RunOn.BackgroundThread) \ No newline at end of file diff --git a/vripper-gui/src/main/kotlin/me/mnlr/vripper/gui/Styles.kt b/vripper-gui/src/main/kotlin/me/mnlr/vripper/gui/Styles.kt new file mode 100644 index 00000000..9a2e7c51 --- /dev/null +++ b/vripper-gui/src/main/kotlin/me/mnlr/vripper/gui/Styles.kt @@ -0,0 +1,30 @@ +package me.mnlr.vripper.gui + +import javafx.scene.paint.Color +import javafx.scene.text.FontWeight +import tornadofx.* + +class Styles : Stylesheet() { + companion object { + val heading by cssclass() + val actionBarButton by cssclass() + } + + init { + label and heading { + padding = box(20.px) + fontSize = 20.px + fontWeight = FontWeight.BOLD + } + + actionBarButton { + prefWidth = 32.px + prefHeight = 32.px + padding = box(0.px) + backgroundColor += Color.TRANSPARENT + and(hover) { + backgroundColor += Color.LIGHTGREY + } + } + } +} \ No newline at end of file diff --git a/vripper-gui/src/main/kotlin/me/mnlr/vripper/model/GlobalStateModel.kt b/vripper-gui/src/main/kotlin/me/mnlr/vripper/model/GlobalStateModel.kt new file mode 100644 index 00000000..2fc0ea61 --- /dev/null +++ b/vripper-gui/src/main/kotlin/me/mnlr/vripper/model/GlobalStateModel.kt @@ -0,0 +1,29 @@ +package me.mnlr.vripper.model + +import javafx.beans.property.SimpleIntegerProperty +import javafx.beans.property.SimpleStringProperty +import tornadofx.getValue +import tornadofx.setValue + +class GlobalStateModel( + running: Int, + remaining: Int, + error: Int, + loggedUser: String, + downloadSpeed: String +) { + val runningProperty = SimpleIntegerProperty(running) + var running: Int by runningProperty + + val remainingProperty = SimpleIntegerProperty(remaining) + var remaining: Int by remainingProperty + + val errorProperty = SimpleIntegerProperty(error) + var error: Int by errorProperty + + val loggedUserProperty = SimpleStringProperty(loggedUser) + var loggedUser: String by loggedUserProperty + + val downloadSpeedProperty = SimpleStringProperty(downloadSpeed) + var downloadSpeed: String by downloadSpeedProperty +} diff --git a/vripper-gui/src/main/kotlin/me/mnlr/vripper/model/ImageModel.kt b/vripper-gui/src/main/kotlin/me/mnlr/vripper/model/ImageModel.kt new file mode 100644 index 00000000..c555de0d --- /dev/null +++ b/vripper-gui/src/main/kotlin/me/mnlr/vripper/model/ImageModel.kt @@ -0,0 +1,31 @@ +package me.mnlr.vripper.model + +import javafx.beans.property.SimpleDoubleProperty +import javafx.beans.property.SimpleIntegerProperty +import javafx.beans.property.SimpleLongProperty +import javafx.beans.property.SimpleStringProperty +import tornadofx.* + +class ImageModel( + id: Long, + index: Int, + url: String, + progress: Double, + status: String, + val postId: String +) { + val idProperty = SimpleLongProperty(id) + var id: Long by idProperty + + val indexProperty = SimpleIntegerProperty(index) + var index: Int by indexProperty + + val urlProperty = SimpleStringProperty(url) + var url: String by urlProperty + + val progressProperty = SimpleDoubleProperty(progress) + var progress: Double by progressProperty + + val statusProperty = SimpleStringProperty(status) + var status: String by statusProperty +} diff --git a/vripper-gui/src/main/kotlin/me/mnlr/vripper/model/LogModel.kt b/vripper-gui/src/main/kotlin/me/mnlr/vripper/model/LogModel.kt new file mode 100644 index 00000000..92112ab1 --- /dev/null +++ b/vripper-gui/src/main/kotlin/me/mnlr/vripper/model/LogModel.kt @@ -0,0 +1,28 @@ +package me.mnlr.vripper.model + +import javafx.beans.property.SimpleLongProperty +import javafx.beans.property.SimpleStringProperty +import tornadofx.* + +class LogModel( + id: Long, + type: String, + status: String, + time: String, + message: String +) { + val idProperty = SimpleLongProperty(id) + var id: Long by idProperty + + val typeProperty = SimpleStringProperty(type) + var type: String by typeProperty + + val statusProperty = SimpleStringProperty(status) + var status: String by statusProperty + + val timeProperty = SimpleStringProperty(time) + var time: String by timeProperty + + val messageProperty = SimpleStringProperty(message) + var message: String by messageProperty +} diff --git a/vripper-gui/src/main/kotlin/me/mnlr/vripper/model/PostModel.kt b/vripper-gui/src/main/kotlin/me/mnlr/vripper/model/PostModel.kt new file mode 100644 index 00000000..feff8b00 --- /dev/null +++ b/vripper-gui/src/main/kotlin/me/mnlr/vripper/model/PostModel.kt @@ -0,0 +1,53 @@ +package me.mnlr.vripper.model + +import javafx.beans.property.SimpleDoubleProperty +import javafx.beans.property.SimpleIntegerProperty +import javafx.beans.property.SimpleStringProperty +import tornadofx.* + +class PostModel( + postId: String, + title: String, + progress: Double, + status: String, + url: String, + done: Int, + total: Int, + hosts: String, + addedOn: String, + order: Int, + path: String +) { + val postIdProperty = SimpleStringProperty(postId) + var postId: String by postIdProperty + + val titleProperty = SimpleStringProperty(title) + var title: String by titleProperty + + val progressProperty = SimpleDoubleProperty(progress) + var progress: Double by progressProperty + + val statusProperty = SimpleStringProperty(status) + var status: String by statusProperty + + val urlProperty = SimpleStringProperty(url) + var url: String by urlProperty + + val doneProperty = SimpleIntegerProperty(done) + var done: Int by doneProperty + + val totalProperty = SimpleIntegerProperty(total) + var total: Int by totalProperty + + val hostsProperty = SimpleStringProperty(hosts) + var hosts: String by hostsProperty + + val addedOnProperty = SimpleStringProperty(addedOn) + var addedOn: String by addedOnProperty + + val orderProperty = SimpleIntegerProperty(order) + var order: Int by orderProperty + + val pathProperty = SimpleStringProperty(path) + var path: String by pathProperty +} diff --git a/vripper-gui/src/main/kotlin/me/mnlr/vripper/model/ThreadModel.kt b/vripper-gui/src/main/kotlin/me/mnlr/vripper/model/ThreadModel.kt new file mode 100644 index 00000000..c7710bf7 --- /dev/null +++ b/vripper-gui/src/main/kotlin/me/mnlr/vripper/model/ThreadModel.kt @@ -0,0 +1,17 @@ +package me.mnlr.vripper.model + +import javafx.beans.property.SimpleIntegerProperty +import javafx.beans.property.SimpleStringProperty +import tornadofx.* + +class ThreadModel( + link: String, + total: Int, + val threadId: String +) { + val linkProperty = SimpleStringProperty(link) + var link: String by linkProperty + + val totalProperty = SimpleIntegerProperty(total) + var total: Int by totalProperty +} diff --git a/vripper-gui/src/main/kotlin/me/mnlr/vripper/model/ThreadSelectionModel.kt b/vripper-gui/src/main/kotlin/me/mnlr/vripper/model/ThreadSelectionModel.kt new file mode 100644 index 00000000..d3f11cee --- /dev/null +++ b/vripper-gui/src/main/kotlin/me/mnlr/vripper/model/ThreadSelectionModel.kt @@ -0,0 +1,27 @@ +package me.mnlr.vripper.model + +import javafx.beans.property.SimpleIntegerProperty +import javafx.beans.property.SimpleStringProperty +import me.mnlr.vripper.host.Host +import tornadofx.* + +class ThreadSelectionModel( + index: Int, + title: String, + url: String, + hosts: Map, + val postId: String, + val threadId: String +) { + val indexProperty = SimpleIntegerProperty(index) + var index: Int by indexProperty + + val titleProperty = SimpleStringProperty(title) + var title: String by titleProperty + + val urlProperty = SimpleStringProperty(url) + var url: String by urlProperty + + val hostsProperty = SimpleStringProperty(hosts.keys.map { it.host }.joinToString(", ")) + var hosts: String by hostsProperty +} diff --git a/vripper-gui/src/main/kotlin/me/mnlr/vripper/model/settings/ConnectionSettingsModel.kt b/vripper-gui/src/main/kotlin/me/mnlr/vripper/model/settings/ConnectionSettingsModel.kt new file mode 100644 index 00000000..75f5c2b4 --- /dev/null +++ b/vripper-gui/src/main/kotlin/me/mnlr/vripper/model/settings/ConnectionSettingsModel.kt @@ -0,0 +1,18 @@ +package me.mnlr.vripper.model.settings + +import javafx.beans.property.* +import tornadofx.* + +class ConnectionSettingsModel { + val maxThreadsProperty = SimpleIntegerProperty() + var maxThreads: Int by maxThreadsProperty + + val maxTotalThreadsProperty = SimpleIntegerProperty() + var maxTotalThreads: Int by maxTotalThreadsProperty + + val timeoutProperty = SimpleIntegerProperty() + var timeout: Int by timeoutProperty + + val maxAttemptsProperty = SimpleIntegerProperty() + var maxAttempts: Int by maxAttemptsProperty +} diff --git a/vripper-gui/src/main/kotlin/me/mnlr/vripper/model/settings/DownloadSettingsModel.kt b/vripper-gui/src/main/kotlin/me/mnlr/vripper/model/settings/DownloadSettingsModel.kt new file mode 100644 index 00000000..8f02e901 --- /dev/null +++ b/vripper-gui/src/main/kotlin/me/mnlr/vripper/model/settings/DownloadSettingsModel.kt @@ -0,0 +1,30 @@ +package me.mnlr.vripper.model.settings + +import javafx.beans.property.* +import tornadofx.* + +class DownloadSettingsModel { + val downloadPathProperty = SimpleStringProperty() + var downloadPath: String by downloadPathProperty + + val autoStartProperty = SimpleBooleanProperty() + var autoStart: Boolean by autoStartProperty + + val autoQueueThresholdProperty = SimpleIntegerProperty() + var autoQueueThreshold: Int by autoQueueThresholdProperty + + val forceOrderProperty = SimpleBooleanProperty() + var forceOrder: Boolean by forceOrderProperty + + val forumSubfolderProperty = SimpleBooleanProperty() + var forumSubfolder: Boolean by forumSubfolderProperty + + val threadSubLocationProperty = SimpleBooleanProperty() + var threadSubLocation: Boolean by threadSubLocationProperty + + val clearCompletedProperty = SimpleBooleanProperty() + var clearCompleted: Boolean by clearCompletedProperty + + val appendPostIdProperty = SimpleBooleanProperty() + var appendPostId: Boolean by appendPostIdProperty +} diff --git a/vripper-gui/src/main/kotlin/me/mnlr/vripper/model/settings/ViperSettingsModel.kt b/vripper-gui/src/main/kotlin/me/mnlr/vripper/model/settings/ViperSettingsModel.kt new file mode 100644 index 00000000..a4f1f414 --- /dev/null +++ b/vripper-gui/src/main/kotlin/me/mnlr/vripper/model/settings/ViperSettingsModel.kt @@ -0,0 +1,21 @@ +package me.mnlr.vripper.model.settings + +import javafx.beans.property.* +import tornadofx.* + +class ViperSettingsModel { + val loginProperty = SimpleBooleanProperty() + var login: Boolean by loginProperty + + val usernameProperty = SimpleStringProperty() + var username: String by usernameProperty + + val passwordProperty = SimpleStringProperty() + var password: String by passwordProperty + + val thanksProperty = SimpleBooleanProperty() + var thanks: Boolean by thanksProperty + + val hostProperty = SimpleStringProperty() + var host: String by hostProperty +} diff --git a/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/AppView.kt b/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/AppView.kt new file mode 100644 index 00000000..3eb9eb5b --- /dev/null +++ b/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/AppView.kt @@ -0,0 +1,17 @@ +package me.mnlr.vripper.view + +import me.mnlr.vripper.view.actionbar.ActionBarView +import me.mnlr.vripper.view.main.MainView +import me.mnlr.vripper.view.status.StatusBarView +import tornadofx.View +import tornadofx.borderpane + +class AppView : View("Vripper") { + + override val root = borderpane { + top() + center() + bottom() + } + +} \ No newline at end of file diff --git a/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/LoadingView.kt b/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/LoadingView.kt new file mode 100644 index 00000000..338ddacd --- /dev/null +++ b/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/LoadingView.kt @@ -0,0 +1,30 @@ +package me.mnlr.vripper.view + +import javafx.scene.control.ProgressIndicator.INDETERMINATE_PROGRESS +import javafx.scene.effect.DropShadow +import javafx.util.Duration +import me.mnlr.vripper.event.ApplicationInitialized +import tornadofx.* + +class LoadingView : View("Vripper") { + + private val appView: AppView by inject() + + init { + subscribe { + runLater { + replaceWith(appView, ViewTransition.FadeThrough(Duration.millis(250.0))) + } + } + } + + override val root = borderpane { + padding = insets(all = 5) + center { + progressindicator { + progress = INDETERMINATE_PROGRESS + } + } + effect = DropShadow() + } +} \ No newline at end of file diff --git a/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/actionbar/ActionBarView.kt b/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/actionbar/ActionBarView.kt new file mode 100644 index 00000000..5ee57840 --- /dev/null +++ b/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/actionbar/ActionBarView.kt @@ -0,0 +1,29 @@ +package me.mnlr.vripper.view.actionbar + +import javafx.scene.control.ContentDisplay +import me.mnlr.vripper.gui.Styles +import me.mnlr.vripper.view.settings.SettingsView +import tornadofx.* + +class ActionBarView : View("Vripper") { + + override val root = borderpane { + padding = insets(all = 5) + left() + center() + right { + button("Settings") { + imageview("settings.png") { + fitWidth = 32.0 + fitHeight = 32.0 + } + addClass(Styles.actionBarButton) + contentDisplay = ContentDisplay.GRAPHIC_ONLY + tooltip("Open settings menu") + action { + find().openModal() + } + } + } + } +} \ No newline at end of file diff --git a/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/actionbar/AddActionsView.kt b/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/actionbar/AddActionsView.kt new file mode 100644 index 00000000..4034d635 --- /dev/null +++ b/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/actionbar/AddActionsView.kt @@ -0,0 +1,31 @@ +package me.mnlr.vripper.view.actionbar + +import javafx.geometry.Orientation +import javafx.scene.control.ContentDisplay +import javafx.scene.input.KeyCode +import javafx.scene.input.KeyCodeCombination +import javafx.scene.input.KeyCombination +import me.mnlr.vripper.gui.Styles +import me.mnlr.vripper.view.posts.AddView +import tornadofx.* + +class AddActionsView : View() { + + override val root = hbox { + button("Add links") { + imageview("plus.png") { + fitWidth = 32.0 + fitHeight = 32.0 + } + addClass(Styles.actionBarButton) + contentDisplay = ContentDisplay.GRAPHIC_ONLY + tooltip("Add new links [Ctrl+L]") + shortcut(KeyCodeCombination(KeyCode.L, KeyCombination.CONTROL_DOWN)) + action { + find().input.clear() + find().openModal() + } + } + separator(Orientation.VERTICAL) + } +} \ No newline at end of file diff --git a/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/actionbar/DownloadActionsView.kt b/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/actionbar/DownloadActionsView.kt new file mode 100644 index 00000000..2668b595 --- /dev/null +++ b/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/actionbar/DownloadActionsView.kt @@ -0,0 +1,134 @@ +package me.mnlr.vripper.view.actionbar + +import javafx.beans.property.SimpleBooleanProperty +import javafx.geometry.Orientation +import javafx.scene.control.ButtonType +import javafx.scene.control.ContentDisplay +import javafx.scene.input.KeyCode +import javafx.scene.input.KeyCodeCombination +import javafx.scene.input.KeyCombination +import me.mnlr.vripper.controller.GlobalStateController +import me.mnlr.vripper.controller.PostController +import me.mnlr.vripper.gui.Styles +import me.mnlr.vripper.view.tables.PostsTableView +import tornadofx.* + +class DownloadActionsView : View() { + + private val postController: PostController by inject() + private val globalStateController: GlobalStateController by inject() + private val postsTableView: PostsTableView by inject() + private val downloadActiveProperty = SimpleBooleanProperty(true) + + init { + downloadActiveProperty.bind(globalStateController.globalState.runningProperty.greaterThan(0)) + } + + override val root = hbox { + button("Start All") { + imageview("end.png") { + fitWidth = 32.0 + fitHeight = 32.0 + } + addClass(Styles.actionBarButton) + contentDisplay = ContentDisplay.GRAPHIC_ONLY + tooltip("Start downloads [Ctrl+S]") + shortcut(KeyCodeCombination(KeyCode.S, KeyCombination.CONTROL_DOWN)) + disableWhen(downloadActiveProperty) + action { + postController.startAll() + } + } + button("Stop All") { + setPrefSize(32.0, 32.0) + imageview("stop.png") { + fitWidth = 32.0 + fitHeight = 32.0 + } + addClass(Styles.actionBarButton) + contentDisplay = ContentDisplay.GRAPHIC_ONLY + tooltip("Stops all running downloads [Ctrl+Q]") + shortcut(KeyCodeCombination(KeyCode.Q, KeyCombination.CONTROL_DOWN)) + disableWhen(downloadActiveProperty.not()) + action { + postController.stopAll() + } + } + separator(Orientation.VERTICAL) + button("Start") { + setPrefSize(32.0, 32.0) + imageview("play.png") { + fitWidth = 32.0 + fitHeight = 32.0 + } + addClass(Styles.actionBarButton) + contentDisplay = ContentDisplay.GRAPHIC_ONLY + tooltip("Start downloads for selected [Ctrl+Shift+S]") + shortcut(KeyCodeCombination(KeyCode.S, KeyCombination.CONTROL_DOWN, KeyCombination.SHIFT_DOWN)) + enableWhen( + postsTableView.tableView.selectionModel.selectedItems.sizeProperty.greaterThan( + 0 + ) + ) + action { + postsTableView.startSelected() + } + } + button("Stop") { + setPrefSize(32.0, 32.0) + imageview("pause.png") { + fitWidth = 32.0 + fitHeight = 32.0 + } + addClass(Styles.actionBarButton) + contentDisplay = ContentDisplay.GRAPHIC_ONLY + tooltip("Stop downloads for selected [Ctrl+Shift+Q]") + shortcut(KeyCodeCombination(KeyCode.Q, KeyCombination.CONTROL_DOWN, KeyCombination.SHIFT_DOWN)) + enableWhen( + postsTableView.tableView.selectionModel.selectedItems.sizeProperty.greaterThan( + 0 + ) + ) + action { + postsTableView.stopSelected() + } + } + button("Delete") { + setPrefSize(32.0, 32.0) + imageview("trash.png") { + fitWidth = 32.0 + fitHeight = 32.0 + } + addClass(Styles.actionBarButton) + contentDisplay = ContentDisplay.GRAPHIC_ONLY + tooltip("Delete selected posts [Del]") + shortcut(KeyCodeCombination(KeyCode.DELETE)) + enableWhen( + postsTableView.tableView.selectionModel.selectedItems.sizeProperty.greaterThan( + 0 + ) + ) + action { + postsTableView.deleteSelected() + } + } + separator(Orientation.VERTICAL) + button("Clear") { + setPrefSize(32.0, 32.0) + imageview("broom.png") { + fitWidth = 32.0 + fitHeight = 32.0 + } + addClass(Styles.actionBarButton) + contentDisplay = ContentDisplay.GRAPHIC_ONLY + tooltip("Clear all finished downloads [Ctrl+Del]") + shortcut(KeyCodeCombination(KeyCode.DELETE, KeyCombination.CONTROL_DOWN)) + action { + confirm("Clean finished posts", "Confirm removal of finished posts", ButtonType.YES, ButtonType.NO) { + val clearPosts = postController.clearPosts() + postsTableView.tableView.items.removeIf { clearPosts.contains(it.postId) } + } + } + } + } +} diff --git a/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/actionbar/LogActionsView.kt b/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/actionbar/LogActionsView.kt new file mode 100644 index 00000000..bc73adcc --- /dev/null +++ b/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/actionbar/LogActionsView.kt @@ -0,0 +1,36 @@ +package me.mnlr.vripper.view.actionbar + +import javafx.scene.control.ButtonType +import javafx.scene.control.ContentDisplay +import javafx.scene.input.KeyCode +import javafx.scene.input.KeyCodeCombination +import javafx.scene.input.KeyCombination +import me.mnlr.vripper.controller.LogController +import me.mnlr.vripper.gui.Styles +import me.mnlr.vripper.view.tables.LogTableView +import tornadofx.* + +class LogActionsView : View() { + + private val logController: LogController by inject() + private val logTableView: LogTableView by inject() + + override val root = hbox { + button("Clear") { + imageview("broom.png") { + fitWidth = 32.0 + fitHeight = 32.0 + } + addClass(Styles.actionBarButton) + contentDisplay = ContentDisplay.GRAPHIC_ONLY + tooltip("Clear logs [Ctrl+Del]") + shortcut(KeyCodeCombination(KeyCode.DELETE, KeyCombination.CONTROL_DOWN)) + action { + confirm("Clear logs", "Are you sure you want to clear the logs", ButtonType.YES, ButtonType.NO) { + logController.clear() + logTableView.items.clear() + } + } + } + } +} \ No newline at end of file diff --git a/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/actionbar/ThreadActionsView.kt b/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/actionbar/ThreadActionsView.kt new file mode 100644 index 00000000..65131be9 --- /dev/null +++ b/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/actionbar/ThreadActionsView.kt @@ -0,0 +1,54 @@ +package me.mnlr.vripper.view.actionbar + +import javafx.scene.control.ButtonType +import javafx.scene.control.ContentDisplay +import javafx.scene.input.KeyCode +import javafx.scene.input.KeyCodeCombination +import javafx.scene.input.KeyCombination +import me.mnlr.vripper.controller.ThreadController +import me.mnlr.vripper.gui.Styles +import me.mnlr.vripper.view.tables.ThreadTableView +import tornadofx.* + +class ThreadActionsView : View() { + + private val threadTableView: ThreadTableView by inject() + private val threadController: ThreadController by inject() + + override val root = hbox { + button("Delete") { + imageview("trash.png") { + fitWidth = 32.0 + fitHeight = 32.0 + } + addClass(Styles.actionBarButton) + contentDisplay = ContentDisplay.GRAPHIC_ONLY + tooltip("Delete selected threads [Del]") + shortcut(KeyCodeCombination(KeyCode.DELETE)) + enableWhen( + threadTableView.tableView.selectionModel.selectedItems.sizeProperty.greaterThan( + 0 + ) + ) + action { + threadTableView.deleteSelected() + } + } + button("Clear") { + imageview("broom.png") { + fitWidth = 32.0 + fitHeight = 32.0 + } + addClass(Styles.actionBarButton) + contentDisplay = ContentDisplay.GRAPHIC_ONLY + tooltip("Clear all threads [Ctrl+Del]") + shortcut(KeyCodeCombination(KeyCode.DELETE, KeyCombination.CONTROL_DOWN)) + action { + confirm("Clean threads", "Confirm removal of threads", ButtonType.YES, ButtonType.NO) { + threadController.clearAll() + threadTableView.tableView.items.clear() + } + } + } + } +} \ No newline at end of file diff --git a/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/main/MainView.kt b/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/main/MainView.kt new file mode 100644 index 00000000..66d8a79c --- /dev/null +++ b/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/main/MainView.kt @@ -0,0 +1,74 @@ +package me.mnlr.vripper.view.main + +import javafx.scene.control.TabPane +import javafx.scene.image.ImageView +import javafx.util.Duration +import me.mnlr.vripper.view.actionbar.ActionBarView +import me.mnlr.vripper.view.actionbar.DownloadActionsView +import me.mnlr.vripper.view.actionbar.LogActionsView +import me.mnlr.vripper.view.actionbar.ThreadActionsView +import me.mnlr.vripper.view.tables.LogTableView +import me.mnlr.vripper.view.tables.PostsTableView +import me.mnlr.vripper.view.tables.ThreadTableView +import tornadofx.* + +class MainView : View("Center view") { + + private val actionBarView: ActionBarView by inject() + + private val downloadActionBar: DownloadActionsView by inject() + private val threadActionsView: ThreadActionsView by inject() + private val logActionsView: LogActionsView by inject() + + override val root = tabpane { + tabClosingPolicy = TabPane.TabClosingPolicy.UNAVAILABLE + selectionModel.selectedItemProperty() + .addListener(ChangeListener { _, _, newValue -> + run { + when (newValue.id) { + "download-tab" -> { + actionBarView.root.center.replaceWith( + downloadActionBar.root, + ViewTransition.FadeThrough(Duration.millis(100.0)) + ) + } + + "thread-tab" -> { + actionBarView.root.center.replaceWith( + threadActionsView.root, + ViewTransition.FadeThrough(Duration.millis(100.0)) + ) + } + + "log-tab" -> { + actionBarView.root.center.replaceWith( + logActionsView.root, + ViewTransition.FadeThrough(Duration.millis(100.0)) + ) + } + } + } + }) + tab { + val imageView = ImageView("download.png") + imageView.fitWidth = 18.0 + imageView.fitHeight = 18.0 + id = "download-tab" + graphic = imageView + } + tab { + val imageView = ImageView("analyze.png") + imageView.fitWidth = 18.0 + imageView.fitHeight = 18.0 + id = "thread-tab" + graphic = imageView + } + tab { + val imageView = ImageView("bug.png") + imageView.fitWidth = 18.0 + imageView.fitHeight = 18.0 + id = "log-tab" + graphic = imageView + } + } +} \ No newline at end of file diff --git a/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/posts/AddView.kt b/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/posts/AddView.kt new file mode 100644 index 00000000..2d22eae2 --- /dev/null +++ b/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/posts/AddView.kt @@ -0,0 +1,36 @@ +package me.mnlr.vripper.view.posts + +import javafx.beans.property.SimpleStringProperty +import javafx.geometry.Pos +import javafx.scene.control.TextArea +import javafx.scene.layout.Priority +import javafx.scene.layout.VBox +import me.mnlr.vripper.controller.PostController +import tornadofx.* + +class AddView : Fragment("Add thread links") { + + private val textAreaProperty = SimpleStringProperty() + private val postController: PostController by inject() + lateinit var input: TextArea + + override val root = vbox(alignment = Pos.CENTER_RIGHT) { + padding = insets(all = 5) + spacing = 5.0 + input = textarea { + VBox.setVgrow(this, Priority.ALWAYS) + bind(textAreaProperty) + } + button("Scan") { + imageview("search.png") { + fitWidth = 18.0 + fitHeight = 18.0 + } + disableWhen(textAreaProperty.isEmpty) + action { + postController.scan(textAreaProperty.value) + close() + } + } + } +} \ No newline at end of file diff --git a/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/settings/ConnectionSettingsView.kt b/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/settings/ConnectionSettingsView.kt new file mode 100644 index 00000000..4107f47d --- /dev/null +++ b/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/settings/ConnectionSettingsView.kt @@ -0,0 +1,45 @@ +package me.mnlr.vripper.view.settings + +import me.mnlr.vripper.controller.SettingsController +import me.mnlr.vripper.model.settings.ConnectionSettingsModel +import tornadofx.* + +class ConnectionSettingsView : View("Connection settings") { + private val settingsController: SettingsController by inject() + val connectionSettingsModel = ConnectionSettingsModel() + + override fun onDock() { + val connectionSettings = settingsController.findConnectionSettings() + connectionSettingsModel.maxThreads = connectionSettings.maxThreads + connectionSettingsModel.maxTotalThreads = connectionSettings.maxTotalThreads + connectionSettingsModel.timeout = connectionSettings.timeout + connectionSettingsModel.maxAttempts = connectionSettings.maxAttempts + } + + override val root = vbox { + form { + fieldset { + field("Concurrent downloads per host") { + textfield(connectionSettingsModel.maxThreadsProperty) { + filterInput { it.controlNewText.isInt() } + } + } + field("Global concurrent downloads") { + textfield(connectionSettingsModel.maxTotalThreadsProperty) { + filterInput { it.controlNewText.isInt() } + } + } + field("Connection timeout") { + textfield(connectionSettingsModel.timeoutProperty) { + filterInput { it.controlNewText.isInt() } + } + } + field("Maximum attempts") { + textfield(connectionSettingsModel.maxAttemptsProperty) { + filterInput { it.controlNewText.isInt() } + } + } + } + } + } +} \ No newline at end of file diff --git a/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/settings/DownloadSettingsView.kt b/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/settings/DownloadSettingsView.kt new file mode 100644 index 00000000..8c05ee2d --- /dev/null +++ b/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/settings/DownloadSettingsView.kt @@ -0,0 +1,79 @@ +package me.mnlr.vripper.view.settings + +import me.mnlr.vripper.controller.SettingsController +import me.mnlr.vripper.model.settings.DownloadSettingsModel +import tornadofx.* + + +class DownloadSettingsView : View("Download Settings") { + + private val settingsController: SettingsController by inject() + val downloadSettingsModel = DownloadSettingsModel() + + override fun onDock() { + val downloadSettings = settingsController.findDownloadSettings() + downloadSettingsModel.downloadPath = downloadSettings.downloadPath + downloadSettingsModel.autoStart = downloadSettings.autoStart + downloadSettingsModel.autoQueueThreshold = downloadSettings.autoQueueThreshold + downloadSettingsModel.forceOrder = downloadSettings.forceOrder + downloadSettingsModel.forumSubfolder = downloadSettings.forumSubfolder + downloadSettingsModel.threadSubLocation = downloadSettings.threadSubLocation + downloadSettingsModel.clearCompleted = downloadSettings.clearCompleted + downloadSettingsModel.appendPostId = downloadSettings.appendPostId + } + + override val root = vbox { + form { + fieldset { + field("Download Path") { + textfield(downloadSettingsModel.downloadPathProperty) { + isEditable = false + } + button("Browse") { + action { + val directory = chooseDirectory(title = "Select download folder") + if(directory != null) { + downloadSettingsModel.downloadPathProperty.set(directory.path) + } + } + } + } + field("Auto start downloads") { + checkbox { + bind(downloadSettingsModel.autoStartProperty) + } + } + field("Auto queue thread if post count is below or equal to") { + textfield(downloadSettingsModel.autoQueueThresholdProperty) { + filterInput { it.controlNewText.isInt() } + } + } + field("Organize by category") { + checkbox { + bind(downloadSettingsModel.forumSubfolderProperty) + } + } + field("Organize by thread") { + checkbox { + bind(downloadSettingsModel.threadSubLocationProperty) + } + } + field("Order images") { + checkbox { + bind(downloadSettingsModel.forceOrderProperty) + } + } + field("Append post id to download folder") { + checkbox { + bind(downloadSettingsModel.appendPostIdProperty) + } + } + field("Clear Finished") { + checkbox { + bind(downloadSettingsModel.clearCompletedProperty) + } + } + } + } + } +} \ No newline at end of file diff --git a/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/settings/SettingsView.kt b/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/settings/SettingsView.kt new file mode 100644 index 00000000..c9a3e79d --- /dev/null +++ b/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/settings/SettingsView.kt @@ -0,0 +1,71 @@ +package me.mnlr.vripper.view.settings + +import javafx.geometry.Pos +import javafx.scene.control.Alert +import javafx.scene.control.TabPane +import javafx.scene.image.ImageView +import javafx.scene.layout.Priority +import javafx.scene.layout.VBox +import me.mnlr.vripper.controller.SettingsController +import me.mnlr.vripper.exception.ValidationException +import tornadofx.* + + +class SettingsView : Fragment("Settings") { + + private val settingsController: SettingsController by inject() + + private val downloadSettingsView: DownloadSettingsView by inject() + private val connectionSettingsView: ConnectionSettingsView by inject() + private val viperSettingsView: ViperSettingsView by inject() + + override val root = vbox(alignment = Pos.CENTER_RIGHT) { + spacing = 5.0 + tabpane { + tabClosingPolicy = TabPane.TabClosingPolicy.UNAVAILABLE + VBox.setVgrow(this, Priority.ALWAYS) + tab() { + val imageView = ImageView("downloads-folder.png") + imageView.fitWidth = 18.0 + imageView.fitHeight = 18.0 + graphic = imageView + } + tab() { + val imageView = ImageView("data-transfer.png") + imageView.fitWidth = 18.0 + imageView.fitHeight = 18.0 + graphic = imageView + } + tab() { + val imageView = ImageView("icons/32x32.png") + imageView.fitWidth = 18.0 + imageView.fitHeight = 18.0 + graphic = imageView + } + } + borderpane { + right { + padding = insets(all = 5.0) + button("Save") { + imageview("save.png") { + fitWidth = 18.0 + fitHeight = 18.0 + } + action { + try { + settingsController.saveNewSettings( + downloadSettingsView.downloadSettingsModel, + connectionSettingsView.connectionSettingsModel, + viperSettingsView.viperSettingsModel + ) + close() + } catch (e: ValidationException) { + alert(Alert.AlertType.ERROR, "Invalid settings", e.message) + } + + } + } + } + } + } +} \ No newline at end of file diff --git a/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/settings/ViperSettingsView.kt b/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/settings/ViperSettingsView.kt new file mode 100644 index 00000000..a9f18136 --- /dev/null +++ b/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/settings/ViperSettingsView.kt @@ -0,0 +1,52 @@ +package me.mnlr.vripper.view.settings + +import javafx.collections.FXCollections +import me.mnlr.vripper.controller.SettingsController +import me.mnlr.vripper.model.settings.ViperSettingsModel +import tornadofx.* + +class ViperSettingsView : View("Viper Settings") { + private val settingsController: SettingsController by inject() + val viperSettingsModel = ViperSettingsModel() + val proxies = FXCollections.observableArrayList() + + override fun onDock() { + proxies.clear() + proxies.addAll(settingsController.getProxies()) + val viperGirlsSettings = settingsController.findViperGirlsSettings() + viperSettingsModel.login = viperGirlsSettings.login + viperSettingsModel.username = viperGirlsSettings.username + viperSettingsModel.password = viperGirlsSettings.password + viperSettingsModel.thanks = viperGirlsSettings.thanks + viperSettingsModel.host = viperGirlsSettings.host + } + + override val root = vbox { + form { + fieldset { + field("Enable ViperGirls Authentication") { + checkbox { + bind(viperSettingsModel.loginProperty) + } + } + fieldset { + visibleWhen(viperSettingsModel.loginProperty) + field("ViperGirls Username") { + textfield(viperSettingsModel.usernameProperty) + } + field("ViperGirls Password") { + passwordfield(viperSettingsModel.passwordProperty) + } + field("Leave like") { + checkbox { + bind(viperSettingsModel.thanksProperty) + } + } + field("Select a proxy") { + combobox(viperSettingsModel.hostProperty, proxies) + } + } + } + } + } +} \ No newline at end of file diff --git a/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/status/StatusBarView.kt b/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/status/StatusBarView.kt new file mode 100644 index 00000000..2e1445bc --- /dev/null +++ b/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/status/StatusBarView.kt @@ -0,0 +1,36 @@ +package me.mnlr.vripper.view.status + +import javafx.geometry.Orientation +import javafx.geometry.Pos +import me.mnlr.vripper.controller.GlobalStateController +import tornadofx.* + +class StatusBarView : View("Status bar") { + + private val globalStateController: GlobalStateController by inject() + + override val root = borderpane { + left { + text(globalStateController.globalState.loggedUserProperty.map { "Logged in as: $it" }) { + visibleWhen(globalStateController.globalState.loggedUserProperty.isNotBlank()) + } + } + right { + padding = insets(right = 5, left = 5, top = 3, bottom = 3) + hbox { + spacing = 3.0 + text(globalStateController.globalState.downloadSpeedProperty.map { "$it/s" }) + separator(Orientation.VERTICAL) + label("Downloading") + text(globalStateController.globalState.runningProperty.asString()) + separator(Orientation.VERTICAL) + label("Pending") + text(globalStateController.globalState.remainingProperty.asString()) + separator(Orientation.VERTICAL) + label("Error") + text(globalStateController.globalState.errorProperty.asString()) + alignment = Pos.CENTER_RIGHT + } + } + } +} \ No newline at end of file diff --git a/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/tables/ImagesTableView.kt b/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/tables/ImagesTableView.kt new file mode 100644 index 00000000..bd1002b2 --- /dev/null +++ b/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/tables/ImagesTableView.kt @@ -0,0 +1,97 @@ +package me.mnlr.vripper.view.tables + +import javafx.collections.FXCollections +import javafx.collections.ObservableList +import javafx.geometry.Pos +import javafx.scene.control.TableView +import javafx.scene.input.MouseButton +import me.mnlr.vripper.controller.ImageController +import me.mnlr.vripper.event.Event +import me.mnlr.vripper.event.EventBus +import me.mnlr.vripper.model.ImageModel +import tornadofx.* + +class ImagesTableView : Fragment("Photos") { + + private lateinit var tableView: TableView + private val imageController: ImageController by inject() + private val eventBus: EventBus by di() + private var items: ObservableList = FXCollections.observableArrayList() + val postId: String by param() + + override fun onDock() { + tableView.prefWidthProperty().bind(root.widthProperty()) + tableView.prefHeightProperty().bind(root.heightProperty()) + modalStage?.width = 600.0 + + runLater { + items.addAll(imageController.findImages(postId)) + tableView.sort() + + val disposable = eventBus.flux() + .filter { it!!.kind == Event.Kind.IMAGE_UPDATE } + .subscribe { event -> + imageController + .findImageById(event!!.data as Long) + .filter { it.postId == postId } + .ifPresent { + // search + val find = items + .find { image -> image.id == it.id } + if (find != null) { + find.apply { + progress = it.progress + status = it.status + } + } else { + items.add(it) + } + } + } + + whenUndocked { + disposable.dispose() + } + } + } + + override val root = vbox(alignment = Pos.CENTER_RIGHT) { + tableView = tableview(items) { + column("Index", ImageModel::indexProperty) { + sortOrder.add(this) + } + column("Link", ImageModel::urlProperty) { + prefWidth = 200.0 + } + column("Progress", ImageModel::progressProperty) { + cellFormat { + addClass(Stylesheet.progressBarTableCell) + graphic = cache { + progressbar(itemProperty().doubleBinding { it?.toDouble() ?: 0.0 }) { + setOnMouseClicked { + when (it.clickCount) { + 1 -> { + this@tableview.requestFocus() + this@tableview.focusModel.focus(this@cellFormat.tableRow.index) + if (it.isControlDown && it.button.equals(MouseButton.PRIMARY)) { + if (this@tableview.selectionModel.isSelected(this@cellFormat.tableRow.index)) { + this@tableview.selectionModel.clearSelection(this@cellFormat.tableRow.index) + } else { + this@tableview.selectionModel.select(this@cellFormat.tableRow.index) + } + } else if (it.button.equals(MouseButton.PRIMARY)) { + this@tableview.selectionModel.clearSelection() + this@tableview.selectionModel.select(this@cellFormat.tableRow.index) + } + } + } + } + useMaxWidth = true + } + } + } + } + column("Status", ImageModel::statusProperty) + } + } +} \ No newline at end of file diff --git a/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/tables/LogTableView.kt b/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/tables/LogTableView.kt new file mode 100644 index 00000000..9974a7ae --- /dev/null +++ b/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/tables/LogTableView.kt @@ -0,0 +1,64 @@ +package me.mnlr.vripper.view.tables + +import javafx.collections.FXCollections +import javafx.collections.ObservableList +import javafx.scene.control.SelectionMode +import javafx.scene.control.TableView +import me.mnlr.vripper.controller.LogController +import me.mnlr.vripper.event.Event +import me.mnlr.vripper.event.EventBus +import me.mnlr.vripper.model.LogModel +import tornadofx.* + +class LogTableView : View("Log") { + + private val logController: LogController by inject() + private val eventBus: EventBus by di() + + lateinit var tableView: TableView + + var items: ObservableList = FXCollections.observableArrayList() + + init { + items.addAll(logController.findAll()) + + eventBus.flux() + .doOnNext { event -> + if (event!!.kind.equals(Event.Kind.LOG_EVENT_UPDATE)) { + logController.find(event.data as Long) + .ifPresent { + // search + val find = items + .find { threadModel -> threadModel.id == it.id } + if (find != null) { + find.apply { + status = it.status + message = it.message + } + } else { + items.add(it) + } + } + } else if (event.kind.equals(Event.Kind.LOG_EVENT_REMOVE)) { + items.removeIf { it.id == event.data } + } + } + .subscribe() + } + + override fun onDock() { + tableView.prefHeightProperty().bind(root.heightProperty()) + } + + override val root = vbox { + tableView = tableview(items) { + selectionModel.selectionMode = SelectionMode.MULTIPLE + column("Time", LogModel::timeProperty) { + sortOrder.add(this) + } + column("Type", LogModel::typeProperty) + column("Status", LogModel::statusProperty) + column("Message", LogModel::messageProperty) + } + } +} diff --git a/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/tables/PostsTableView.kt b/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/tables/PostsTableView.kt new file mode 100644 index 00000000..d95d1a13 --- /dev/null +++ b/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/tables/PostsTableView.kt @@ -0,0 +1,178 @@ +package me.mnlr.vripper.view.tables + +import javafx.collections.FXCollections +import javafx.collections.ObservableList +import javafx.scene.control.* +import javafx.scene.image.ImageView +import javafx.scene.input.MouseButton +import me.mnlr.vripper.controller.PostController +import me.mnlr.vripper.event.Event +import me.mnlr.vripper.event.EventBus +import me.mnlr.vripper.model.PostModel +import tornadofx.* + + +class PostsTableView : View("Download") { + + private val postController: PostController by inject() + private val eventBus: EventBus by di() + + lateinit var tableView: TableView + private var items: ObservableList = FXCollections.observableArrayList() + + init { + items.addAll(postController.findAllPosts()) + + eventBus.flux().filter { + it!!.kind == Event.Kind.POST_UPDATE || it.kind == Event.Kind.METADATA_UPDATE + }.subscribe { event -> + postController.findById(event!!.data as Long).ifPresent { + // search + val find = items.find { postModel -> postModel.postId == it.postId } + if (find != null) { + find.apply { + progress = it.progress + status = it.status + done = it.done + total = it.total + order = it.order + } + } else { + items.add(it) + runLater { + this.tableView.refresh() + } + } + } + + } + } + + override fun onDock() { + tableView.prefHeightProperty().bind(root.heightProperty()) + } + + override val root = vbox { + tableView = tableview(items) { + selectionModel.selectionMode = SelectionMode.MULTIPLE + setRowFactory { + val tableRow = TableRow() + tableRow.setOnMouseClicked { + if (it.clickCount == 2 && tableRow.item != null) { + openPhotos(tableRow.item.postId) + } + } + + val contextMenu = ContextMenu() + val startItem = MenuItem("Start") + startItem.setOnAction { + startSelected() + } + val playIcon = ImageView("play.png") + playIcon.fitWidth = 18.0 + playIcon.fitHeight = 18.0 + startItem.graphic = playIcon + + val stopItem = MenuItem("Stop") + stopItem.setOnAction { + stopSelected() + } + val stopIcon = ImageView("pause.png") + stopIcon.fitWidth = 18.0 + stopIcon.fitHeight = 18.0 + stopItem.graphic = stopIcon + + val deleteItem = MenuItem("Delete") + deleteItem.setOnAction { + deleteSelected() + } + val deleteIcon = ImageView("trash.png") + deleteIcon.fitWidth = 18.0 + deleteIcon.fitHeight = 18.0 + deleteItem.graphic = deleteIcon + + val detailsItem = MenuItem("Images") + detailsItem.setOnAction { + openPhotos(tableRow.item.postId) + } + val detailsIcon = ImageView("details.png") + detailsIcon.fitWidth = 18.0 + detailsIcon.fitHeight = 18.0 + detailsItem.graphic = detailsIcon + + contextMenu.items.addAll( + startItem, stopItem, deleteItem, SeparatorMenuItem(), detailsItem + ) + tableRow.contextMenuProperty() + .bind(tableRow.emptyProperty().map { empty -> if (empty) null else contextMenu }) + tableRow + } + column("Title", PostModel::titleProperty) { + prefWidth = 300.0 + } + column("Progress", PostModel::progressProperty) { + cellFormat { + addClass(Stylesheet.progressBarTableCell) + graphic = cache { + progressbar(itemProperty().doubleBinding { it?.toDouble() ?: 0.0 }) { + setOnMouseClicked { + when (it.clickCount) { + 1 -> { + this@tableview.requestFocus() + this@tableview.focusModel.focus(this@cellFormat.tableRow.index) + if (it.isControlDown && it.button.equals(MouseButton.PRIMARY)) { + if (this@tableview.selectionModel.isSelected(this@cellFormat.tableRow.index)) { + this@tableview.selectionModel.clearSelection(this@cellFormat.tableRow.index) + } else { + this@tableview.selectionModel.select(this@cellFormat.tableRow.index) + } + } else if (it.button.equals(MouseButton.PRIMARY)) { + this@tableview.selectionModel.clearSelection() + this@tableview.selectionModel.select(this@cellFormat.tableRow.index) + } + } + 2 -> openPhotos(this@cellFormat.tableRow.item.postId) + } + } + useMaxWidth = true + } + } + } + } + column("Status", PostModel::statusProperty) + column("Path", PostModel::pathProperty) + column("Images", PostModel::totalProperty) + column("Added On", PostModel::addedOnProperty) + column("Order", PostModel::orderProperty) { + sortOrder.add(this) + } + } + } + + fun deleteSelected() { + val postIdList = tableView.selectionModel.selectedItems.map { it.postId } + confirm( + "Remove posts", + "Confirm removal of ${postIdList.size} post${if (postIdList.size > 1) "s" else ""}", + ButtonType.YES, + ButtonType.NO + ) { + postController.delete(postIdList) + tableView.items.removeIf { postIdList.contains(it.postId) } + } + } + + fun stopSelected() { + val postIdList = tableView.selectionModel.selectedItems.map { it.postId } + postController.stop(postIdList) + } + + fun startSelected() { + val postIdList = tableView.selectionModel.selectedItems.map { it.postId } + postController.start(postIdList) + } + + private fun openPhotos(postId: String) { + find(mapOf(ImagesTableView::postId to postId)).openModal() + } +} \ No newline at end of file diff --git a/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/tables/ThreadSelectionTableView.kt b/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/tables/ThreadSelectionTableView.kt new file mode 100644 index 00000000..6c9e27d9 --- /dev/null +++ b/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/tables/ThreadSelectionTableView.kt @@ -0,0 +1,73 @@ +package me.mnlr.vripper.view.tables + +import javafx.collections.FXCollections +import javafx.collections.ObservableList +import javafx.geometry.Pos +import javafx.scene.control.Label +import javafx.scene.control.SelectionMode +import javafx.scene.control.TableRow +import javafx.scene.control.TableView +import me.mnlr.vripper.controller.ThreadController +import me.mnlr.vripper.model.ThreadSelectionModel +import tornadofx.* + +class ThreadSelectionTableView : Fragment("Thread") { + + private lateinit var tableView: TableView + private val threadController: ThreadController by inject() + private var items: ObservableList = FXCollections.observableArrayList() + val threadId: String by param() + + override fun onDock() { + tableView.prefWidthProperty().bind(root.widthProperty()) + tableView.prefHeightProperty().bind(root.heightProperty()) + modalStage?.width = 600.0 + tableView.placeholder = Label("Loading") + + runLater { + items.addAll(threadController.grab(threadId)) + } + } + + override val root = vbox(alignment = Pos.CENTER_RIGHT) { + spacing = 5.0 + tableView = tableview(items) { + selectionModel.selectionMode = SelectionMode.MULTIPLE + setRowFactory { + val tableRow = TableRow() + + tableRow.setOnMouseClicked { + if (it.clickCount == 2 && tableRow.item != null) { + threadController.download(listOf(tableRow.item)) + close() + } + } + + tableRow + } + column("Post Index", ThreadSelectionModel::indexProperty) { + sortOrder.add(this) + } + column("Title", ThreadSelectionModel::titleProperty) { prefWidth = 200.0 } + column("URL", ThreadSelectionModel::urlProperty) { prefWidth = 200.0 } + column("Hosts", ThreadSelectionModel::hostsProperty) + } + borderpane { + right { + padding = insets(top = 0, right = 5, bottom = 5, left = 5) + button("Download") { + imageview("download.png") { + fitWidth = 18.0 + fitHeight = 18.0 + } + tooltip("Download selected posts") + enableWhen { tableView.selectionModel.selectedItems.sizeProperty.greaterThan(0) } + action { + threadController.download(tableView.selectionModel.selectedItems) + close() + } + } + } + } + } +} \ No newline at end of file diff --git a/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/tables/ThreadTableView.kt b/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/tables/ThreadTableView.kt new file mode 100644 index 00000000..9d37dd9c --- /dev/null +++ b/vripper-gui/src/main/kotlin/me/mnlr/vripper/view/tables/ThreadTableView.kt @@ -0,0 +1,110 @@ +package me.mnlr.vripper.view.tables + +import javafx.collections.FXCollections +import javafx.collections.ObservableList +import javafx.scene.control.* +import javafx.scene.image.ImageView +import me.mnlr.vripper.controller.ThreadController +import me.mnlr.vripper.event.Event +import me.mnlr.vripper.event.EventBus +import me.mnlr.vripper.model.ThreadModel +import me.mnlr.vripper.view.main.MainView +import tornadofx.* + +class ThreadTableView : View("Threads") { + + private val threadController: ThreadController by inject() + private val eventBus: EventBus by di() + + lateinit var tableView: TableView + + private var items: ObservableList = FXCollections.observableArrayList() + + init { + items.addAll(threadController.findAll()) + + eventBus.flux().filter { it!!.kind == Event.Kind.THREAD_UPDATE }.subscribe { event -> + threadController.find(event!!.data as Long).ifPresent { + // search + val find = items.find { threadModel -> threadModel.threadId == it.threadId } + if (find != null) { + find.apply { + total = it.total + } + } else { + items.add(it) + runLater { + find().root.selectionModel.select(1) + tableView.selectionModel.clearSelection() + tableView.selectionModel.select(it) + } + } + } + } + } + + override fun onDock() { + tableView.prefHeightProperty().bind(root.heightProperty()) + } + + override val root = vbox { + tableView = tableview(items) { + selectionModel.selectionMode = SelectionMode.MULTIPLE + setRowFactory { + val tableRow = TableRow() + + tableRow.setOnMouseClicked { + if (it.clickCount == 2 && tableRow.item != null) { + selectPosts(tableRow.item.threadId) + } + } + + val contextMenu = ContextMenu() + val selectItem = MenuItem("Select posts") + selectItem.setOnAction { + selectPosts(tableRow.item.threadId) + } + val selectIcon = ImageView("popup.png") + selectIcon.fitWidth = 18.0 + selectIcon.fitHeight = 18.0 + selectItem.graphic = selectIcon + + val deleteItem = MenuItem("Delete") + deleteItem.setOnAction { + deleteSelected() + } + val deleteIcon = ImageView("trash.png") + deleteIcon.fitWidth = 18.0 + deleteIcon.fitHeight = 18.0 + deleteItem.graphic = deleteIcon + + contextMenu.items.addAll(selectItem, SeparatorMenuItem(), deleteItem) + tableRow.contextMenuProperty() + .bind(tableRow.emptyProperty().map { empty -> if (empty) null else contextMenu }) + tableRow + } + column("URL", ThreadModel::linkProperty) { + prefWidth = 350.0 + } + column("Count", ThreadModel::totalProperty) + } + } + + fun deleteSelected() { + val threadIdList = tableView.selectionModel.selectedItems.map { it.threadId } + confirm( + "Clean threads", + "Confirm removal of ${threadIdList.size} thread${if (threadIdList.size > 1) "s" else ""}", + ButtonType.YES, + ButtonType.NO + ) { + threadController.delete(threadIdList) + tableView.items.removeIf { threadIdList.contains(it.threadId) } + } + } + + private fun selectPosts(threadId: String) { + find(mapOf(ThreadSelectionTableView::threadId to threadId)).openModal() + + } +} \ No newline at end of file diff --git a/vripper-gui/src/main/resources/analyze.png b/vripper-gui/src/main/resources/analyze.png new file mode 100644 index 0000000000000000000000000000000000000000..48b24106ded9ab3886f7cd98caeaf32eb5b686fd GIT binary patch literal 918 zcmV;H18Mw;P)N1lDP6O7+1P55E5gkChevPy)!^) zEsqpqT4>s$q$n-WjBY@Tk;l>~#hnPHv4XKIREm)pLjt&w@Delz6F6pu&P~D6*7iP% z=}As9cQJR)H|H_u2trYcQk1`h#I`|3?GV%!B9rlb?|*9pL1Y4$+t`7JT0jt)02Y4k zY6C%J1keP6$PA!51W^D$RS2RGfNBs#K>$@Ch{6B|A%sE&$X10SzyT?GAoibxP*DYRaRD>(G1`M znTJ658`Rb}F@tEBw2#bY^p-Ca2dmx5_8^3o%w}_mVgQ-K#^z{chSa<6u-MLaZ&!4U z?MXmPi=uc;b%2(hVd>`YMpofm;7eMMrAl~<-ND1DSrLF#hMe_$gw9v1Qo_=ZDDPR! zm@ldR-##+Sy|7_2nNF$#aHVe%yn|sWVclb!aN0SO699>ALt`SbtE#Gy>;Q);t@HFy z3Uc+)*Nn!e1Mu{ZaW6bm4S>fkEtUDaA0)QTN^AmPPIiD)HyN1v#a>r>D6R4N0t6>F zIl$JD1n7Rf!7I^s*#QnyS~}$lA^+%J2w{vrsRlqBGnT{y<8UqCB!2vo13Xo+j83pa zX>ZdS-hvWmJZFFgcLxVBtNM^t^M`DBCaY4K>i@n_oEyI`isA{`0n%wpPlz3EX}k%6 z$O>E}^ZB$Aj+~9p6(IQL2iP39q-Ks9ly&8a?rY{gXwuA4?VLzzr|nMg4aYO?@rB~j z%_D?X5kfl%A)6)udl1WDOCD%Ee%2n)^2i4ox@H57TS{0EMe#GncsKFAD`H%$2>=f# z*MsL$VyDv@YaNSu8a5bX!aRX7T&gfYX@w!A2|)6o(mE7j@5J>REz(d=*KDXCia>XC zJ$b0tA%yQ!x-Ahzeqh4+_4j(3zkCElM&Y z=~GcDeK3MAg7K+#F|l?7@guEKlUmfM)u!6U**npgkDc-3rBo3~Ar(roolR1TnvZUF z&7IjJGm}j>-I>j9cCx#|{^7jL%-;L|-Fwctmqnlmxw`Ow*l} zoLg*(HHwmYUXaL~Ad~*4U87q{9>g{tOPJsWy@)ELhvkvm1X;Zz$|~f<*MR~k27N;@ z=vBp_2T;HgBy!?9^B~G(zT|=Iv~eNmMKVpF$bA|6rWOV6(w@n3!qUN7E!HBl+{7c)tAU=RO@P#NQ-yss36EOIgm*PCxKTC z+NC7ubSyy=ib13HvvS9MOggz$kkwgl^JEG|B5WW4w&81C$qoBix#J3w`i0->?`R== zQBZPMJ`Z4~Ec0@#**A40bw+$H-Vhjooi=#@32Ky;I+8l$3(%szf`XJ+3dIb1u$bQp zlDgA5pd|9ED3cLUBJGWy2bQ|B`ebfH*M}mCIO^_1u|*7wD5*U@hm)uIm3LjJ7G$rq zzsYQ$SFi6p=|`G}?x?nRBs?A4E=VM`E`Xc|&|huu2o9sIzjtmw-Q0TY?+pP&0_y!6 zQL-Q1xfEsoN$x{%HNs@RZ5Pis$)}b}1nH}ac^Jp{rru%aZ1cIX(7`Qtx4rO3DG)nk z_0h^12gE=e?*R=jm_r;SUtJ53joHqd=i7F7|5;PD0P!0h0-gT>bPU-r2RT@J%^Sjo zJG=j$%TnvWI`u)6O*D;e0UbXN8vk>y@ij4l!92-`uOHwpZ3~Y zzEQp5F3@og=r{^G?gE`301f{FI;OtHJt8y0!%_$1xg+x(VS1=-?>UdVP%wWafQCsa zX8poac}PXQA(HzLT+V1?u;bV*@SL`r?qgpH)ch*M0NzG>nUwhf3+Bs+nJf z7~mc>kFb;U&1=O?-$HJ2`{leF!*+{CwIcd?sEPKo7tBvXR)Lhsrr0_AQiy6%5Wn$u z=k(U_q)*40wi^NZKgjOw&edDT$3VlsfyO5w&OK0VUv<@0S6wI&{RgsqZZA|K;xhmM N002ovPDHLkV1j()Jr)1} literal 0 HcmV?d00001 diff --git a/vripper-gui/src/main/resources/bug.png b/vripper-gui/src/main/resources/bug.png new file mode 100644 index 0000000000000000000000000000000000000000..3de697f0e15eaa83e3dd16cddf38420eb8ee3104 GIT binary patch literal 1563 zcmV+$2ITpPP)0|bnT0pbS)6u7H`5Ca6zs3+Xa^`yI1 z{iYAyC5L85C z1WM60vo{CZJCHl=-Svc!6($5Q~aZI#yLo|&g zf|Y!P_4n2qLCYFcz0Y_|H1!tI)OQP}aa=IXoyWun=JM9bLD9F(bFu#7IyTM*i{;Z+|br&n8gL@;$lFpY0}ENJRSsyd9yT=i91 z#vIWyj`lQK|3s(sq+ppbadOimIkW2I;rKVKta?4R4wcGnFJ-#ILEJ4O(rZ(qlq^l<^T5?hs7lH%`LOGmVqAf>v?_=u1GDgsN~;R<_`qn}yO=mL_?oT9GLil;ztH zCQCx0P_LaSIRjz324QL=Om<|B1(L1P$FG0Y9@re|9`qdP_HI~Z2i|L)nkfe{RhSMa z(RYhx__f8OlaNF%J0xT}*F9FgaQ+6+RBO9i`7=+i=`ml9_z|XmBTTvjm}ZeH3J|8R zr94P}h{a+9t`0QSp3Woy7Sx_{&xM$L$-Te26t;6CXpD!^i5>%)#;2PL2-6OS0H!fe z3e3WhF;F6ZG9v<*3i%T7*HACSn7naA9fgEH zOoBXgu6nN`vGs0GLS5udC_Bce8|<;In{2RlnbX*B*2l(}43lMZglKdKlIZu>eT#(z z!t|?@=g8sN^BYHcHAVGm$`x-ywcRaEAhX|*)fBZ-j#MB_4sd6D*CP=W5)hNmQj0nZ zcUSD(l4bnA5=f{vn}eJ*n$iO?{k&)d003nuOlPwL2x8hbVf!XKUjh@hZDdAwF&#`7 z(+Ns}V5ok?{o6O4&X)k26Aab&joQegDF^c~F#FM$WwulY29Nll=Zj4ws}mqCf{hZ6aVWssRIEqQNZHV3&fP$(2CFPin0VLFj7 z&Z@~14=r8VnJdd{x~C(?hghR4Vt=Y<)K^&Oj*6^eRtjCb2$b|q`qJT91@r=a2r3XCOQscdeqm{ zobx93Em+K)(BUp6cxX~kOfWAZOiwY-`DJ?2`;KzqrUbMt7m?iX3;!Pex@1KhLr|up zcu1XL_e0)C?=d29St!($L9J%b)~yzgi?c#XOK?z?9r6Qn-O3#Ocr7CvGXNX z)9`S~2n6Ivgona5_I%%tjlP3g)c;7O>L$Hm`bm8XM%L}gyc?JOAqB#|Rd97|-n zyWgX!FJ=+grM%#lfij)R6Tl~+6hOG)iS)%*)~BIumsMy=bNRZLudwDxD2p@y2xN0m zYXAHW!;xvlGrYETD3ZwL^o;_$q4?jo8xpz9_Jq$Dy9E>)>7$0CA!vESNEU) zaq-pF4Mv!}&UB>?L-IH%1`sAMp>T9Yujva%XCO?M_m;kX^aJFMe*oy*9t;Oh+AaV9 N002ovPDHLkV1g%O+|&R7 literal 0 HcmV?d00001 diff --git a/vripper-gui/src/main/resources/data-transfer.png b/vripper-gui/src/main/resources/data-transfer.png new file mode 100644 index 0000000000000000000000000000000000000000..42f19635975c8a348b80fc0fff811d60c2eeb1f5 GIT binary patch literal 471 zcmV;|0Vw{7P)7bBp5O=pceVg@|-;Rk0GZ5}^gHV)~c+!Cf+h|0B6M|BW#okEbij<@yj? zw`~wvisC5}y=3c}1`P`0Jc+wY!ovxW=mv{EwZg*)@cP9tS+|=G4kNG_oy?HvRlD## zFThsIEZXQ4K8t0(M{|IzZj!i{tON5;1s2wh$I0qZ)l( zjG#r42^O@lMQZ<)7A0Y{i9C0J#bKqsw_2U_!3gC9M1?RmSGW`xHQ4g^1L1oj;20(tvMZz1tn{+~4bm3{j z33-ch06F4D+UKYN$PPI0H0~sRo@s@;OYsojzO|0wbRUODK@V@Zu%OQcN1I^jgDlPf zfURar2zR0pbF@kL5O6Hqh4s`lLaAx;%p5@QWCDh?XJ!BrnIOt?5viNVJHY8p1d?1r zJiSIgfFzelXhl^}$ORzg0#H?z)Jl~?U4W)vC4{$N@pM3kcnDSZ{f7-wJGfY-A0Uwl zqN0?6yX7jt{R*jRaMS}xMhhttirG07v$If@3QH}tMuMK!3~`jyzFL8%OR&l|Dwqzk zP-=>p$%9N_?J8lb+2RKe(1`D@ zrUyRj0rE*8e+-;;lbU%YsJlw=rnVsfTJX}f?)(v{ncK+*NdCbJ)V5H!z6#pgXQhRB rD-1Djg>96#!a>Hfc9pPY>!0Wq>O%~MK{b^;00000NkvXXu0mjfW|Rl4 literal 0 HcmV?d00001 diff --git a/vripper-gui/src/main/resources/download.png b/vripper-gui/src/main/resources/download.png new file mode 100644 index 0000000000000000000000000000000000000000..08ce08553cc899750a71bc1808565ddbf70a2d5d GIT binary patch literal 388 zcmV-~0ek+5P)HXLNSRdziqzNG`bH9a7Uj83IUU6U7ASCj#iL4*xZ(atO*DZ(v zvFw)aJ*$V9AnVEKAFXyFI-PtPot}+Qor~fXgBa0)%p&XEkeh!fUN4AQws*5atWUpm z{&~S)uR;otuhx;}?Z?f(FR^SuO2})QoO~9Oaeo_9L@dYTKvePn{qyJxQTLIy60L!8B(UaNtW;8s!8B`=UXqYiUe928x*LJu;u-GGr_>XG6u3H? z#bB!FT7Do^L?J(~<_X{@d&YvRz`mJQcZAT8we5|j^2=MBYTp4gmzB=MEZoIMJO#caZnX%c$SN{UHGAiMmuDD;VY-S> X6PqMAVtFig00000NkvXXu0mjfvwG4x literal 0 HcmV?d00001 diff --git a/vripper-gui/src/main/resources/end.png b/vripper-gui/src/main/resources/end.png new file mode 100644 index 0000000000000000000000000000000000000000..206dc95be7f33f215ec97c3fa7ac9bb88a67563d GIT binary patch literal 285 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%0DJEp}8t-9A%}FK(EK?MSEbh=*KxX@xme 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