From de2aa68a457788b41df49f27f5c29fd7a0b31488 Mon Sep 17 00:00:00 2001 From: ch4mpy Date: Fri, 21 Jul 2023 17:01:30 -1000 Subject: [PATCH] First working version (somehow) --- angular-ui/README.md | 10 +- angular-ui/package-lock.json | 3875 +++++++++++------ angular-ui/package.json | 5 +- .../c4-soft/bff-api/.openapi-generator/FILES | 3 - .../c4-soft/quiz-api/.openapi-generator/FILES | 10 +- .../quiz/src/app/app-routing.module.ts | 12 +- .../projects/quiz/src/app/app.component.ts | 2 +- .../projects/quiz/src/app/app.module.ts | 80 +- .../quiz/src/app/choice-item.component.ts | 188 + .../quiz/src/app/confirmation.dialog.ts | 33 + .../projects/quiz/src/app/error.dialog.ts | 38 + .../quiz/src/app/question-creation.dialog.ts | 86 + .../question-expansion-pannel.component.ts | 280 ++ .../quiz/src/app/quiz-creation.dialog.ts | 107 + .../quiz/src/app/quiz-details.page.ts | 531 +++ .../quiz/src/app/quiz-rejection.dialog.ts | 41 + .../quiz/src/app/quiz-selection.page.ts | 200 + .../quiz/src/app/skill-test-details.page.ts | 118 + .../quiz/src/app/skill-test-result.dialog.ts | 20 + .../quiz/src/app/skill-test-selection.page.ts | 211 + .../quiz/src/app/toolbar.component.ts | 25 +- .../projects/quiz/src/app/user.service.ts | 55 +- angular-ui/projects/quiz/src/styles.scss | 21 + api/bff/pom.xml | 29 +- .../java/com/c4soft/quiz/BffApplication.java | 7 + .../java/com/c4soft/quiz/BffController.java | 35 +- api/bff/src/main/resources/application.yml | 33 +- api/bff/src/main/resources/banner.txt | 22 +- api/pom.xml | 19 +- api/quiz-api/pom.xml | 43 +- .../java/com/c4soft/quiz/SecurityConfig.java | 22 +- .../java/com/c4soft/quiz/domain/Choice.java | 7 +- .../domain/DraftAlreadyExistsException.java | 15 + .../quiz/domain/NotADraftException.java | 15 + .../java/com/c4soft/quiz/domain/Question.java | 15 +- .../java/com/c4soft/quiz/domain/Quiz.java | 62 +- .../quiz/domain/QuizAuthentication.java | 31 + .../c4soft/quiz/domain/QuizRejectionDto.java | 7 + .../c4soft/quiz/domain/QuizRepository.java | 43 +- .../com/c4soft/quiz/domain/SkillTest.java | 81 +- .../quiz/domain/SkillTestRepository.java | 21 +- .../com/c4soft/quiz/feign/FeignConfig.java | 9 + .../quiz/feign/KeycloakAdminApiClient.java | 41 + .../com/c4soft/quiz/web/QuizController.java | 544 ++- .../c4soft/quiz/web/SkillTestController.java | 153 +- .../com/c4soft/quiz/web/UsersController.java | 34 + .../quiz/web/dto/QuestionUpdateDto.java | 4 + .../java/com/c4soft/quiz/web/dto/QuizDto.java | 33 +- .../c4soft/quiz/web/dto/QuizUpdateDto.java | 16 +- .../com/c4soft/quiz/web/dto/SkillTestDto.java | 7 +- .../web/dto/SkillTestResultDetailsDto.java | 12 + .../quiz/web/dto/SkillTestResultDto.java | 5 - .../web/dto/SkillTestResultPreviewDto.java | 5 + .../com/c4soft/quiz/web/dto/UserInfoDto.java | 14 + ...itional-spring-configuration-metadata.json | 10 + .../src/main/resources/application.yml | 46 +- api/quiz-api/src/main/resources/banner.txt | 22 +- .../c4soft/quiz/QuizApiApplicationTest.java | 276 +- api/quiz-api/src/test/resources/ch4mp.json | 15 +- 59 files changed, 5708 insertions(+), 1996 deletions(-) create mode 100644 angular-ui/projects/quiz/src/app/choice-item.component.ts create mode 100644 angular-ui/projects/quiz/src/app/confirmation.dialog.ts create mode 100644 angular-ui/projects/quiz/src/app/error.dialog.ts create mode 100644 angular-ui/projects/quiz/src/app/question-creation.dialog.ts create mode 100644 angular-ui/projects/quiz/src/app/question-expansion-pannel.component.ts create mode 100644 angular-ui/projects/quiz/src/app/quiz-creation.dialog.ts create mode 100644 angular-ui/projects/quiz/src/app/quiz-details.page.ts create mode 100644 angular-ui/projects/quiz/src/app/quiz-rejection.dialog.ts create mode 100644 angular-ui/projects/quiz/src/app/quiz-selection.page.ts create mode 100644 angular-ui/projects/quiz/src/app/skill-test-details.page.ts create mode 100644 angular-ui/projects/quiz/src/app/skill-test-result.dialog.ts create mode 100644 angular-ui/projects/quiz/src/app/skill-test-selection.page.ts create mode 100644 api/quiz-api/src/main/java/com/c4soft/quiz/domain/DraftAlreadyExistsException.java create mode 100644 api/quiz-api/src/main/java/com/c4soft/quiz/domain/NotADraftException.java create mode 100644 api/quiz-api/src/main/java/com/c4soft/quiz/domain/QuizAuthentication.java create mode 100644 api/quiz-api/src/main/java/com/c4soft/quiz/domain/QuizRejectionDto.java create mode 100644 api/quiz-api/src/main/java/com/c4soft/quiz/feign/FeignConfig.java create mode 100644 api/quiz-api/src/main/java/com/c4soft/quiz/feign/KeycloakAdminApiClient.java create mode 100644 api/quiz-api/src/main/java/com/c4soft/quiz/web/UsersController.java create mode 100644 api/quiz-api/src/main/java/com/c4soft/quiz/web/dto/SkillTestResultDetailsDto.java delete mode 100644 api/quiz-api/src/main/java/com/c4soft/quiz/web/dto/SkillTestResultDto.java create mode 100644 api/quiz-api/src/main/java/com/c4soft/quiz/web/dto/SkillTestResultPreviewDto.java create mode 100644 api/quiz-api/src/main/java/com/c4soft/quiz/web/dto/UserInfoDto.java diff --git a/angular-ui/README.md b/angular-ui/README.md index 178910e..fad588c 100644 --- a/angular-ui/README.md +++ b/angular-ui/README.md @@ -1,6 +1,12 @@ -# AngularUi +# C4 - Quiz -This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 16.0.0. +A simple app around multiple choices questionnaires. + +Instructors can define quizzes and trainees have those tests. + +No account is required to answer a quiz (and see expected answers), but trainees must be identified to submit their result to the author of the quiz. Only the last submission of each trainee is stored. + +An "instructor" account is required to create a quiz. Instructors may edit the quizzes they authored and see all the submitted answers. ## Development server diff --git a/angular-ui/package-lock.json b/angular-ui/package-lock.json index fe936a2..4cee2dd 100644 --- a/angular-ui/package-lock.json +++ b/angular-ui/package-lock.json @@ -15,16 +15,19 @@ "@angular/core": "^16.0.0", "@angular/forms": "^16.0.0", "@angular/material": "^16.1.5", + "@angular/material-moment-adapter": "^16.2.6", "@angular/platform-browser": "^16.0.0", "@angular/platform-browser-dynamic": "^16.0.0", "@angular/router": "^16.0.0", + "@capacitor/cli": "^5.3.0", + "@capacitor/core": "^5.3.0", "rxjs": "~7.8.0", "tslib": "^2.3.0", "zone.js": "~0.13.0" }, "devDependencies": { "@angular-devkit/build-angular": "^16.0.6", - "@angular/cli": "~16.0.0", + "@angular/cli": "^16.2.0", "@angular/compiler-cli": "^16.0.0", "@openapitools/openapi-generator-cli": "^2.6.0", "@types/jasmine": "~4.3.0", @@ -52,12 +55,12 @@ } }, "node_modules/@angular-devkit/architect": { - "version": "0.1600.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1600.6.tgz", - "integrity": "sha512-Mk/pRujuer5qRMrgC7DPwLQ88wTAEKhbs0yJ/1prm4cx+VkxX9MMf6Y4AHKRmduKmFmd2LmX21/ACiU65acH8w==", + "version": "0.1602.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1602.0.tgz", + "integrity": "sha512-ZRmUTBeD+uGr605eOHnsovEn6f1mOBI+kxP64DRvagNweX5TN04s3iyQ8jmLSAHQD9ush31LFxv3dVNxv3ceXQ==", "dev": true, "dependencies": { - "@angular-devkit/core": "16.0.6", + "@angular-devkit/core": "16.2.0", "rxjs": "7.8.1" }, "engines": { @@ -356,9 +359,9 @@ } }, "node_modules/@angular-devkit/core": { - "version": "16.0.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-16.0.6.tgz", - "integrity": "sha512-pHbDUwXDMTWTnX/vafkFnzvYDQD8lz+w8FvMQE23Q/vN6/Q0BRf0PWTAGla6Wt+E4HaqqrbQS5P0YBwS4te2Pw==", + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-16.2.0.tgz", + "integrity": "sha512-l1k6Rqm3YM16BEn3CWyQKrk9xfu+2ux7Bw3oS+h1TO4/RoxO2PgHj8LLRh/WNrYVarhaqO7QZ5ePBkXNMkzJ1g==", "dev": true, "dependencies": { "ajv": "8.12.0", @@ -382,14 +385,14 @@ } }, "node_modules/@angular-devkit/schematics": { - "version": "16.0.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-16.0.6.tgz", - "integrity": "sha512-Ipd3uEPgR0qz9HYQvY3RpWHO1DH34mQ6AShKiBypCCd/iwJPcJLKUVon2wYEfKlspgg9N8qWIuoMVHZG0Vwqgg==", + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-16.2.0.tgz", + "integrity": "sha512-QMDJXPE0+YQJ9Ap3MMzb0v7rx6ZbBEokmHgpdIjN3eILYmbAdsSGE8HTV8NjS9nKmcyE9OGzFCMb7PFrDTlTAw==", "dev": true, "dependencies": { - "@angular-devkit/core": "16.0.6", + "@angular-devkit/core": "16.2.0", "jsonc-parser": "3.2.0", - "magic-string": "0.30.0", + "magic-string": "0.30.1", "ora": "5.4.1", "rxjs": "7.8.1" }, @@ -399,6 +402,18 @@ "yarn": ">= 1.13.0" } }, + "node_modules/@angular-devkit/schematics/node_modules/magic-string": { + "version": "0.30.1", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.1.tgz", + "integrity": "sha512-mbVKXPmS0z0G4XqFDCTllmDQ6coZzn94aMlb0o/A4HEHJCKcanlDZwYJgwnkmgD3jyWhUgj9VsPrfd972yPffA==", + "dev": true, + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.4.15" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/@angular/animations": { "version": "16.1.5", "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-16.1.5.tgz", @@ -414,9 +429,9 @@ } }, "node_modules/@angular/cdk": { - "version": "16.1.5", - "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-16.1.5.tgz", - "integrity": "sha512-8wjYhLwW9bWicBYSdDXuN71SBP7NbJmXs+XiWiRkaFUkVDeU9z8Qkitogl+qqsSXvsOmi+12MowrbJ3tPizaLw==", + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-16.2.6.tgz", + "integrity": "sha512-vSaPs69xutbxc6IbZz4I5fMzZhlypsMg5JKKNAufmyYNNHQYgSQytpUd1/RxHhPF/JoEvj/J8QjauRriZFN+SA==", "dependencies": { "tslib": "^2.3.0" }, @@ -430,27 +445,27 @@ } }, "node_modules/@angular/cli": { - "version": "16.0.6", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-16.0.6.tgz", - "integrity": "sha512-um7oOWSu9SIzvwqJ5Aeqcki5/qj4yb6QKi8RkHDWpOdrg1tJfX/BnIzUa4jiCXIwYRIz+PjYJb8W5216wS+7Gg==", + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-16.2.0.tgz", + "integrity": "sha512-xT8vJOyw6Rc2364XDW2jHagLgKu7342ktd/lt+c0u6R+AB2XVFMePR7VceLohX9N/vRUsbQ0nVSZr+ru/hA+HA==", "dev": true, "dependencies": { - "@angular-devkit/architect": "0.1600.6", - "@angular-devkit/core": "16.0.6", - "@angular-devkit/schematics": "16.0.6", - "@schematics/angular": "16.0.6", + "@angular-devkit/architect": "0.1602.0", + "@angular-devkit/core": "16.2.0", + "@angular-devkit/schematics": "16.2.0", + "@schematics/angular": "16.2.0", "@yarnpkg/lockfile": "1.1.0", "ansi-colors": "4.1.3", - "ini": "4.0.0", + "ini": "4.1.1", "inquirer": "8.2.4", "jsonc-parser": "3.2.0", "npm-package-arg": "10.1.0", "npm-pick-manifest": "8.0.1", "open": "8.4.2", "ora": "5.4.1", - "pacote": "15.1.3", + "pacote": "15.2.0", "resolve": "1.22.2", - "semver": "7.4.0", + "semver": "7.5.4", "symbol-observable": "4.0.0", "yargs": "17.7.2" }, @@ -558,62 +573,62 @@ } }, "node_modules/@angular/material": { - "version": "16.1.5", - "resolved": "https://registry.npmjs.org/@angular/material/-/material-16.1.5.tgz", - "integrity": "sha512-l11mH/WWBmfiBhrf4/0hCihhLxK4Ldu7+fP8zucHO3X2TiLlpsgJZpcYwJkZf0Ai0rDqIzqCVnks7L9jiuTGCQ==", - "dependencies": { - "@material/animation": "15.0.0-canary.b994146f6.0", - "@material/auto-init": "15.0.0-canary.b994146f6.0", - "@material/banner": "15.0.0-canary.b994146f6.0", - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/button": "15.0.0-canary.b994146f6.0", - "@material/card": "15.0.0-canary.b994146f6.0", - "@material/checkbox": "15.0.0-canary.b994146f6.0", - "@material/chips": "15.0.0-canary.b994146f6.0", - "@material/circular-progress": "15.0.0-canary.b994146f6.0", - "@material/data-table": "15.0.0-canary.b994146f6.0", - "@material/density": "15.0.0-canary.b994146f6.0", - "@material/dialog": "15.0.0-canary.b994146f6.0", - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/drawer": "15.0.0-canary.b994146f6.0", - "@material/elevation": "15.0.0-canary.b994146f6.0", - "@material/fab": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/floating-label": "15.0.0-canary.b994146f6.0", - "@material/form-field": "15.0.0-canary.b994146f6.0", - "@material/icon-button": "15.0.0-canary.b994146f6.0", - "@material/image-list": "15.0.0-canary.b994146f6.0", - "@material/layout-grid": "15.0.0-canary.b994146f6.0", - "@material/line-ripple": "15.0.0-canary.b994146f6.0", - "@material/linear-progress": "15.0.0-canary.b994146f6.0", - "@material/list": "15.0.0-canary.b994146f6.0", - "@material/menu": "15.0.0-canary.b994146f6.0", - "@material/menu-surface": "15.0.0-canary.b994146f6.0", - "@material/notched-outline": "15.0.0-canary.b994146f6.0", - "@material/radio": "15.0.0-canary.b994146f6.0", - "@material/ripple": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/segmented-button": "15.0.0-canary.b994146f6.0", - "@material/select": "15.0.0-canary.b994146f6.0", - "@material/shape": "15.0.0-canary.b994146f6.0", - "@material/slider": "15.0.0-canary.b994146f6.0", - "@material/snackbar": "15.0.0-canary.b994146f6.0", - "@material/switch": "15.0.0-canary.b994146f6.0", - "@material/tab": "15.0.0-canary.b994146f6.0", - "@material/tab-bar": "15.0.0-canary.b994146f6.0", - "@material/tab-indicator": "15.0.0-canary.b994146f6.0", - "@material/tab-scroller": "15.0.0-canary.b994146f6.0", - "@material/textfield": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/tooltip": "15.0.0-canary.b994146f6.0", - "@material/top-app-bar": "15.0.0-canary.b994146f6.0", - "@material/touch-target": "15.0.0-canary.b994146f6.0", - "@material/typography": "15.0.0-canary.b994146f6.0", + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-16.2.6.tgz", + "integrity": "sha512-JFP12dLrsKwrQ4zZtSRJarYosnxikxLD2M9hfUtHVxgTJr7rdSQ8eE7G2l2zPALSUt+d44MWgQ79xu6inuvEOw==", + "dependencies": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/auto-init": "15.0.0-canary.bc9ae6c9c.0", + "@material/banner": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/button": "15.0.0-canary.bc9ae6c9c.0", + "@material/card": "15.0.0-canary.bc9ae6c9c.0", + "@material/checkbox": "15.0.0-canary.bc9ae6c9c.0", + "@material/chips": "15.0.0-canary.bc9ae6c9c.0", + "@material/circular-progress": "15.0.0-canary.bc9ae6c9c.0", + "@material/data-table": "15.0.0-canary.bc9ae6c9c.0", + "@material/density": "15.0.0-canary.bc9ae6c9c.0", + "@material/dialog": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/drawer": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/fab": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/floating-label": "15.0.0-canary.bc9ae6c9c.0", + "@material/form-field": "15.0.0-canary.bc9ae6c9c.0", + "@material/icon-button": "15.0.0-canary.bc9ae6c9c.0", + "@material/image-list": "15.0.0-canary.bc9ae6c9c.0", + "@material/layout-grid": "15.0.0-canary.bc9ae6c9c.0", + "@material/line-ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/linear-progress": "15.0.0-canary.bc9ae6c9c.0", + "@material/list": "15.0.0-canary.bc9ae6c9c.0", + "@material/menu": "15.0.0-canary.bc9ae6c9c.0", + "@material/menu-surface": "15.0.0-canary.bc9ae6c9c.0", + "@material/notched-outline": "15.0.0-canary.bc9ae6c9c.0", + "@material/radio": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/segmented-button": "15.0.0-canary.bc9ae6c9c.0", + "@material/select": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/slider": "15.0.0-canary.bc9ae6c9c.0", + "@material/snackbar": "15.0.0-canary.bc9ae6c9c.0", + "@material/switch": "15.0.0-canary.bc9ae6c9c.0", + "@material/tab": "15.0.0-canary.bc9ae6c9c.0", + "@material/tab-bar": "15.0.0-canary.bc9ae6c9c.0", + "@material/tab-indicator": "15.0.0-canary.bc9ae6c9c.0", + "@material/tab-scroller": "15.0.0-canary.bc9ae6c9c.0", + "@material/textfield": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tooltip": "15.0.0-canary.bc9ae6c9c.0", + "@material/top-app-bar": "15.0.0-canary.bc9ae6c9c.0", + "@material/touch-target": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.3.0" }, "peerDependencies": { "@angular/animations": "^16.0.0 || ^17.0.0", - "@angular/cdk": "16.1.5", + "@angular/cdk": "16.2.6", "@angular/common": "^16.0.0 || ^17.0.0", "@angular/core": "^16.0.0 || ^17.0.0", "@angular/forms": "^16.0.0 || ^17.0.0", @@ -621,6 +636,19 @@ "rxjs": "^6.5.3 || ^7.4.0" } }, + "node_modules/@angular/material-moment-adapter": { + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/@angular/material-moment-adapter/-/material-moment-adapter-16.2.6.tgz", + "integrity": "sha512-lV+eUp1PVHU2BU+RMAxHqQTwUelOA9go/0t/hFGDVhF9eTGZyzl6CnWgmksPjQCnZTrW3Pb1BhqsizcjLfh1Gw==", + "dependencies": { + "tslib": "^2.3.0" + }, + "peerDependencies": { + "@angular/core": "^16.0.0 || ^17.0.0", + "@angular/material": "16.2.6", + "moment": "^2.18.1" + } + }, "node_modules/@angular/platform-browser": { "version": "16.1.5", "resolved": "https://registry.npmjs.org/@angular/platform-browser/-/platform-browser-16.1.5.tgz", @@ -2432,6 +2460,117 @@ "node": ">=6.9.0" } }, + "node_modules/@capacitor/cli": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@capacitor/cli/-/cli-5.3.0.tgz", + "integrity": "sha512-ku23HPqUHUnSgo/SyEWxVviEAxb4ieWvAVMI3KfrrBoinAhTOvNSZwT346rIpxZ9Xj3Qp41UjdIz0ME+DYwhfA==", + "dependencies": { + "@ionic/cli-framework-output": "^2.2.5", + "@ionic/utils-fs": "^3.1.6", + "@ionic/utils-subprocess": "^2.1.11", + "@ionic/utils-terminal": "^2.3.3", + "commander": "^9.3.0", + "debug": "^4.3.4", + "env-paths": "^2.2.0", + "kleur": "^4.1.4", + "native-run": "^1.7.2", + "open": "^8.4.0", + "plist": "^3.0.5", + "prompts": "^2.4.2", + "rimraf": "^4.4.1", + "semver": "^7.3.7", + "tar": "^6.1.11", + "tslib": "^2.4.0", + "xml2js": "^0.5.0" + }, + "bin": { + "cap": "bin/capacitor", + "capacitor": "bin/capacitor" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@capacitor/cli/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@capacitor/cli/node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "engines": { + "node": "^12.20.0 || >=14" + } + }, + "node_modules/@capacitor/cli/node_modules/glob": { + "version": "9.3.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.5.tgz", + "integrity": "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==", + "dependencies": { + "fs.realpath": "^1.0.0", + "minimatch": "^8.0.2", + "minipass": "^4.2.4", + "path-scurry": "^1.6.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@capacitor/cli/node_modules/minimatch": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.4.tgz", + "integrity": "sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@capacitor/cli/node_modules/minipass": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz", + "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/@capacitor/cli/node_modules/rimraf": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-4.4.1.tgz", + "integrity": "sha512-Gk8NlF062+T9CqNGn6h4tls3k6T1+/nXdOcSZVikNVtlRdYpA7wRJJMoXmuvOnLW844rPjdQ7JgXCYM6PPC/og==", + "dependencies": { + "glob": "^9.2.0" + }, + "bin": { + "rimraf": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@capacitor/core": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@capacitor/core/-/core-5.3.0.tgz", + "integrity": "sha512-mvhh1yJtcUTZ0hUUriBKKpxq47hn75bjxH3tYPRgAFu1z3gowCg+OtG4Rce3W5gr5fSfCjQgOSL0Vp7k9hPUWw==", + "dependencies": { + "tslib": "^2.1.0" + } + }, "node_modules/@colors/colors": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", @@ -2802,6 +2941,155 @@ "node": ">=12" } }, + "node_modules/@ionic/cli-framework-output": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/@ionic/cli-framework-output/-/cli-framework-output-2.2.6.tgz", + "integrity": "sha512-YLPRwnk5Lw0XQ9pKWG+p2KoR5HjMBigZ6yv+/XtL3TGOnCS1+oAz56ABbAORCjTWhSJQisr8APNFiELAecY6QA==", + "dependencies": { + "@ionic/utils-terminal": "2.3.4", + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-array": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@ionic/utils-array/-/utils-array-2.1.6.tgz", + "integrity": "sha512-0JZ1Zkp3wURnv8oq6Qt7fMPo5MpjbLoUoa9Bu2Q4PJuSDWM8H8gwF3dQO7VTeUj3/0o1IB1wGkFWZZYgUXZMUg==", + "dependencies": { + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-fs": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@ionic/utils-fs/-/utils-fs-3.1.7.tgz", + "integrity": "sha512-2EknRvMVfhnyhL1VhFkSLa5gOcycK91VnjfrTB0kbqkTFCOXyXgVLI5whzq7SLrgD9t1aqos3lMMQyVzaQ5gVA==", + "dependencies": { + "@types/fs-extra": "^8.0.0", + "debug": "^4.0.0", + "fs-extra": "^9.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-fs/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@ionic/utils-fs/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@ionic/utils-fs/node_modules/universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@ionic/utils-object": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@ionic/utils-object/-/utils-object-2.1.6.tgz", + "integrity": "sha512-vCl7sl6JjBHFw99CuAqHljYJpcE88YaH2ZW4ELiC/Zwxl5tiwn4kbdP/gxi2OT3MQb1vOtgAmSNRtusvgxI8ww==", + "dependencies": { + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-process": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/@ionic/utils-process/-/utils-process-2.1.11.tgz", + "integrity": "sha512-Uavxn+x8j3rDlZEk1X7YnaN6wCgbCwYQOeIjv/m94i1dzslqWhqIHEqxEyeE8HsT5Negboagg7GtQiABy+BLbA==", + "dependencies": { + "@ionic/utils-object": "2.1.6", + "@ionic/utils-terminal": "2.3.4", + "debug": "^4.0.0", + "signal-exit": "^3.0.3", + "tree-kill": "^1.2.2", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-stream": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@ionic/utils-stream/-/utils-stream-3.1.6.tgz", + "integrity": "sha512-4+Kitey1lTA1yGtnigeYNhV/0tggI3lWBMjC7tBs1K9GXa/q7q4CtOISppdh8QgtOhrhAXS2Igp8rbko/Cj+lA==", + "dependencies": { + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-subprocess": { + "version": "2.1.12", + "resolved": "https://registry.npmjs.org/@ionic/utils-subprocess/-/utils-subprocess-2.1.12.tgz", + "integrity": "sha512-N05Y+dIXBHofKWJTheCMzVqmgY9wFmZcRv/LdNnfXaaA/mxLTyGxQYeig8fvQXTtDafb/siZXcrTkmQ+y6n3Yg==", + "dependencies": { + "@ionic/utils-array": "2.1.6", + "@ionic/utils-fs": "3.1.7", + "@ionic/utils-process": "2.1.11", + "@ionic/utils-stream": "3.1.6", + "@ionic/utils-terminal": "2.3.4", + "cross-spawn": "^7.0.3", + "debug": "^4.0.0", + "tslib": "^2.0.1" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@ionic/utils-terminal": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@ionic/utils-terminal/-/utils-terminal-2.3.4.tgz", + "integrity": "sha512-cEiMFl3jklE0sW60r8JHH3ijFTwh/jkdEKWbylSyExQwZ8pPuwoXz7gpkWoJRLuoRHHSvg+wzNYyPJazIHfoJA==", + "dependencies": { + "@types/slice-ansi": "^4.0.0", + "debug": "^4.0.0", + "signal-exit": "^3.0.3", + "slice-ansi": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "tslib": "^2.0.1", + "untildify": "^4.0.0", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -3003,754 +3291,754 @@ } }, "node_modules/@material/animation": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/animation/-/animation-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-kqqzG54tabYJ5VsBur5k1bqCFQCEpaW3hmLRMiSVVxRY7XgTt7qkuOOz48gs+MPqR6P8VIi6gFpuscV1+DWDhw==", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/animation/-/animation-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-leRf+BcZTfC/iSigLXnYgcHAGvFVQveoJT5+2PIRdyPI/bIG7hhciRgacHRsCKC0sGya81dDblLgdkjSUemYLw==", "dependencies": { "tslib": "^2.1.0" } }, "node_modules/@material/auto-init": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/auto-init/-/auto-init-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-8nLe/XeueJg5yyYx5e4UxWQXpTDyUhibKfyroGwnRKc8pdpOCOulHSOj/fIVGJAIbxkEJoebwMadWUNCjUhc9A==", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/auto-init/-/auto-init-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-uxzDq7q3c0Bu1pAsMugc1Ik9ftQYQqZY+5e2ybNplT8gTImJhNt4M2mMiMHbMANk2l3UgICmUyRSomgPBWCPIA==", "dependencies": { - "@material/base": "15.0.0-canary.b994146f6.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "node_modules/@material/banner": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/banner/-/banner-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-gJ4/VdP4dJgHP72Kdjy2f/UjHB45J4CuxoGvI0NIQYUjOSsr4kQiQHsjVgyEPZR/5wa7kBhM7/0mJ+zF7Ghv2A==", - "dependencies": { - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/button": "15.0.0-canary.b994146f6.0", - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/elevation": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/ripple": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/shape": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/tokens": "15.0.0-canary.b994146f6.0", - "@material/typography": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/banner/-/banner-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-SHeVoidCUFVhXANN6MNWxK9SZoTSgpIP8GZB7kAl52BywLxtV+FirTtLXkg/8RUkxZRyRWl7HvQ0ZFZa7QQAyA==", + "dependencies": { + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/button": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "node_modules/@material/base": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/base/-/base-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-rW2upYD5YjRFBL6DzYn3SCRhtvpEDkwplDS810e3vt71uLMRyqXyw4OQJH+Nab/t+32TFDtKNUphXIzwICXGDQ==", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/base/-/base-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-Fc3vGuOf+duGo22HTRP6dHdc+MUe0VqQfWOuKrn/wXKD62m0QQR2TqJd3rRhCumH557T5QUyheW943M3E+IGfg==", "dependencies": { "tslib": "^2.1.0" } }, "node_modules/@material/button": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/button/-/button-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-SMyqtsvJuCqpXBz2JgciuR6wddNJSGpTXUFxmLbGluBy5/hHm06JWlOFcUOxGDv46OdRGGrRfkg6A9JtvtsJsw==", - "dependencies": { - "@material/density": "15.0.0-canary.b994146f6.0", - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/elevation": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/focus-ring": "15.0.0-canary.b994146f6.0", - "@material/ripple": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/shape": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/tokens": "15.0.0-canary.b994146f6.0", - "@material/touch-target": "15.0.0-canary.b994146f6.0", - "@material/typography": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/button/-/button-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-3AQgwrPZCTWHDJvwgKq7Cj+BurQ4wTjDdGL+FEnIGUAjJDskwi1yzx5tW2Wf/NxIi7IoPFyOY3UB41jwMiOrnw==", + "dependencies": { + "@material/density": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/focus-ring": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/touch-target": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "node_modules/@material/card": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/card/-/card-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-WSggGon91HcDhJyatnYLFkoM9glkkeJjyjFDWrcJkwN1rdrPJU+GH+PNjvmArz5hGv9WkmjDjhOdAuPnL4Mb7g==", - "dependencies": { - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/elevation": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/ripple": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/shape": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/tokens": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/card/-/card-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-nPlhiWvbLmooTnBmV5gmzB0eLWSgLKsSRBYAbIBmO76Okgz1y+fQNLag+lpm/TDaHVsn5fmQJH8e0zIg0rYsQA==", + "dependencies": { + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "node_modules/@material/checkbox": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/checkbox/-/checkbox-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-pulRiwG9S/dS6WBG+GteODBltddFiL0Sb7HAqdzF2BTKNKv25q1ZIR3ftoEa09TNeWM88AOzTJ4aBHiADfJn2w==", - "dependencies": { - "@material/animation": "15.0.0-canary.b994146f6.0", - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/density": "15.0.0-canary.b994146f6.0", - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/focus-ring": "15.0.0-canary.b994146f6.0", - "@material/ripple": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/touch-target": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/checkbox/-/checkbox-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-4tpNnO1L0IppoMF3oeQn8F17t2n0WHB0D7mdJK9rhrujen/fLbekkIC82APB3fdGtLGg3qeNqDqPsJm1YnmrwA==", + "dependencies": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/density": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/focus-ring": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/touch-target": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "node_modules/@material/chips": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/chips/-/chips-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-3yJPj7x+eKLA4LMKG7aTWI+itAnKRVGOcniuR6aiXVy0OKr5asNuWNeZc9J0/VErjjxF3tdybDzDSPo01qPy9w==", - "dependencies": { - "@material/animation": "15.0.0-canary.b994146f6.0", - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/checkbox": "15.0.0-canary.b994146f6.0", - "@material/density": "15.0.0-canary.b994146f6.0", - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/elevation": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/focus-ring": "15.0.0-canary.b994146f6.0", - "@material/ripple": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/shape": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/tokens": "15.0.0-canary.b994146f6.0", - "@material/touch-target": "15.0.0-canary.b994146f6.0", - "@material/typography": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/chips/-/chips-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-fqHKvE5bSWK0bXVkf57MWxZtytGqYBZvvHIOs4JI9HPHEhaJy4CpSw562BEtbm3yFxxALoQknvPW2KYzvADnmA==", + "dependencies": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/checkbox": "15.0.0-canary.bc9ae6c9c.0", + "@material/density": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/focus-ring": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/touch-target": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", "safevalues": "^0.3.4", "tslib": "^2.1.0" } }, "node_modules/@material/circular-progress": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/circular-progress/-/circular-progress-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-6YUvGXdtZKJoE7AuovR4xk1aiWp/EDZ6j2U3TOeynd1assQQCg5XT4abqAoHtpJrRPaCFgUAp836HyiDVVuYug==", - "dependencies": { - "@material/animation": "15.0.0-canary.b994146f6.0", - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/progress-indicator": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/circular-progress/-/circular-progress-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-Lxe8BGAxQwCQqrLhrYrIP0Uok10h7aYS3RBXP41ph+5GmwJd5zdyE2t93qm2dyThvU6qKuXw9726Dtq/N+wvZQ==", + "dependencies": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/progress-indicator": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "node_modules/@material/data-table": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/data-table/-/data-table-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-v4hIduIe/wzyibuL/RPM/ErYrt8XpB7fxyQqtV+0JsMpFa8E81QYyvMCS9EJj9m4YdkrQnZgA+vXQlOkhWvmdQ==", - "dependencies": { - "@material/animation": "15.0.0-canary.b994146f6.0", - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/checkbox": "15.0.0-canary.b994146f6.0", - "@material/density": "15.0.0-canary.b994146f6.0", - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/elevation": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/icon-button": "15.0.0-canary.b994146f6.0", - "@material/linear-progress": "15.0.0-canary.b994146f6.0", - "@material/list": "15.0.0-canary.b994146f6.0", - "@material/menu": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/select": "15.0.0-canary.b994146f6.0", - "@material/shape": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/tokens": "15.0.0-canary.b994146f6.0", - "@material/touch-target": "15.0.0-canary.b994146f6.0", - "@material/typography": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/data-table/-/data-table-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-j/7qplT9+sUpfe4pyWhPbl01qJA+OoNAG3VMJruBBR461ZBKyTi7ssKH9yksFGZ8eCEPkOsk/+kDxsiZvRWkeQ==", + "dependencies": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/checkbox": "15.0.0-canary.bc9ae6c9c.0", + "@material/density": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/icon-button": "15.0.0-canary.bc9ae6c9c.0", + "@material/linear-progress": "15.0.0-canary.bc9ae6c9c.0", + "@material/list": "15.0.0-canary.bc9ae6c9c.0", + "@material/menu": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/select": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/touch-target": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "node_modules/@material/density": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/density/-/density-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-m8l0vuoWSoAPItBpWp5eZDvitUcB2JWoO8V486hLgdveVcKgXG09xWM43ScH+PLXAWjzr5olDEuJ2tvfkN3SpQ==", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/density/-/density-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-Zt3u07fXrBWLW06Tl5fgvjicxNQMkFdawLyNTzZ5TvbXfVkErILLePwwGaw8LNcvzqJP6ABLA8jiR+sKNoJQCg==", "dependencies": { "tslib": "^2.1.0" } }, "node_modules/@material/dialog": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/dialog/-/dialog-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-JucU92yh8cfZQpyRBunHr6uohacePLYmhcPaGpkAGQ1b+zCznEsNs55tjhaVQNoj91XA9rrBqtL6Otg+fxFJtQ==", - "dependencies": { - "@material/animation": "15.0.0-canary.b994146f6.0", - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/button": "15.0.0-canary.b994146f6.0", - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/elevation": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/icon-button": "15.0.0-canary.b994146f6.0", - "@material/ripple": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/shape": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/tokens": "15.0.0-canary.b994146f6.0", - "@material/touch-target": "15.0.0-canary.b994146f6.0", - "@material/typography": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/dialog/-/dialog-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-o+9a/fmwJ9+gY3Z/uhj/PMVJDq7it1NTWKJn2GwAKdB+fDkT4hb9qEdcxMPyvJJ5ups+XiKZo03+tZrD+38c1w==", + "dependencies": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/button": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/icon-button": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/touch-target": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "node_modules/@material/dom": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/dom/-/dom-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-DiUsTezrCi4iytjIn7xXoXZSNFvuTrVVZgc7cR9cW8yu2Hpz8bPf87PacVn4IP9OsNwy/dCDMk1Kcq/DMh7gXQ==", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/dom/-/dom-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-ly78R7aoCJtundSUu0UROU+5pQD5Piae0Y1MkN6bs0724azeazX1KeXFeaf06JOXnlr5/41ol+fSUPowjoqnOg==", "dependencies": { - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "node_modules/@material/drawer": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/drawer/-/drawer-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-Kbuf32V0eX69amvCVbAjNSabNDerZWyG8ip466EfQHRh0OUZwvsbhLp9FZOB7AyR+/bQiHf3mVLcombOdmdkcQ==", - "dependencies": { - "@material/animation": "15.0.0-canary.b994146f6.0", - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/elevation": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/list": "15.0.0-canary.b994146f6.0", - "@material/ripple": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/shape": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/typography": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/drawer/-/drawer-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-PFL4cEFnt7VTxDsuspFVNhsFDYyumjU0VWfj3PWB7XudsEfQ3lo85D3HCEtTTbRsCainGN8bgYNDNafLBqiigw==", + "dependencies": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/list": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "node_modules/@material/elevation": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/elevation/-/elevation-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-l2YDNgBajSI6oA2l6gaeYCTGHRao657syqQ/tv95/Hkcee9900A4RrsxCwSxOqqAs5pZZDEJ33kFJjj27nqZDw==", - "dependencies": { - "@material/animation": "15.0.0-canary.b994146f6.0", - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/elevation/-/elevation-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-Ro+Pk8jFuap+T0B0shA3xI1hs2b89dNQ2EIPCNjNMp87emHKAzJfhKb7EZGIwv3+gFLlVaLyIVkb94I89KLsyg==", + "dependencies": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "node_modules/@material/fab": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/fab/-/fab-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-ExyDVkNWINpns41Ahj4u8I/OhiVkqI0nmcqjFRtgTJMmKEd4NhlvqIxE7gakAlyS68riJu5UleqTSTVmt8mv2Q==", - "dependencies": { - "@material/animation": "15.0.0-canary.b994146f6.0", - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/elevation": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/focus-ring": "15.0.0-canary.b994146f6.0", - "@material/ripple": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/shape": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/tokens": "15.0.0-canary.b994146f6.0", - "@material/touch-target": "15.0.0-canary.b994146f6.0", - "@material/typography": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/fab/-/fab-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-dvU0KWMRglwJEQwmQtFAmJcAjzg9VFF6Aqj78bJYu/DAIGFJ1VTTTSgoXM/XCm1YyQEZ7kZRvxBO37CH54rSDg==", + "dependencies": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/focus-ring": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/touch-target": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "node_modules/@material/feature-targeting": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/feature-targeting/-/feature-targeting-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-HR/FjSQmza98B1DF80MRjODyfOI9r7wXkPSts/cLQsYkpwZ5uJmxhvQKjDCeYVpMV0lQuvuvVOQo7uD44TdWEg==", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/feature-targeting/-/feature-targeting-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-wkDjVcoVEYYaJvun28IXdln/foLgPD7n9ZC9TY76GErGCwTq+HWpU6wBAAk+ePmpRFDayw4vI4wBlaWGxLtysQ==", "dependencies": { "tslib": "^2.1.0" } }, "node_modules/@material/floating-label": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/floating-label/-/floating-label-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-g64talBNWCS0FUfLWal0uB637gUciSIqYxFzSW//LglTtbZLGK2J4+9gAEswQGnKeO4ux08EN2n1ZcMDYQ58ow==", - "dependencies": { - "@material/animation": "15.0.0-canary.b994146f6.0", - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/typography": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/floating-label/-/floating-label-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-bUWPtXzZITOD/2mkvLkEPO1ngDWmb74y0Kgbz6llHLOQBtycyJIpuoQJ1q2Ez0NM/tFLwPphhAgRqmL3YQ/Kzw==", + "dependencies": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "node_modules/@material/focus-ring": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/focus-ring/-/focus-ring-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-87qEMuXsCvlQfTiimnzJUZoebnIXWcMtRZevNLymN9Y0t9jGckQxZPmrI0llRkpyiR/Ewhec5SI/JGrFlYHnsA==", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/focus-ring/-/focus-ring-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-cZHThVose3GvAlJzpJoBI1iqL6d1/Jj9hXrR+r8Mwtb1hBIUEG3hxfsRd4vGREuzROPlf0OgNf/V+YHoSwgR5w==", "dependencies": { - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0" + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0" } }, "node_modules/@material/form-field": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/form-field/-/form-field-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-Tg1SQQaopvXMyDEYxGTWnhCWQmNcWVIoKMLmle9P/gi2p8ulcj0iOCPYf+3ECqUBVozOmTPKlYOOiRwtKStAeA==", - "dependencies": { - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/ripple": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/typography": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/form-field/-/form-field-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-+JFXy5X44Gue1CbZZAQ6YejnI203lebYwL0i6k0ylDpWHEOdD5xkF2PyHR28r9/65Ebcbwbff6q7kI1SGoT7MA==", + "dependencies": { + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "node_modules/@material/icon-button": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/icon-button/-/icon-button-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-X6DvOv4jpymHUjI7ZAbO946nDgGYKDwPZfkRzBE84gv2XEr2qfMuABhojxkYubRbt03oauBdcJVVMFCXkVhArQ==", - "dependencies": { - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/density": "15.0.0-canary.b994146f6.0", - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/elevation": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/focus-ring": "15.0.0-canary.b994146f6.0", - "@material/ripple": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/touch-target": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/icon-button/-/icon-button-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-1a0MHgyIwOs4RzxrVljsqSizGYFlM1zY2AZaLDsgT4G3kzsplTx8HZQ022GpUCjAygW+WLvg4z1qAhQHvsbqlw==", + "dependencies": { + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/density": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/focus-ring": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/touch-target": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "node_modules/@material/image-list": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/image-list/-/image-list-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-kf903XFF1P+V5ZPXCt+7R6c55g4UyQE1ZHkTViCIJfd52gU40bHODMhTQy/ywBkwDeJfNk8uf1V1IM24WQYpxA==", - "dependencies": { - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/shape": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/typography": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/image-list/-/image-list-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-WKWmiYap2iu4QdqmeUSliLlN4O2Ueqa0OuVAYHn/TCzmQ2xmnhZ1pvDLbs6TplpOmlki7vFfe+aSt5SU9gwfOQ==", + "dependencies": { + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "node_modules/@material/layout-grid": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/layout-grid/-/layout-grid-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-OALBSGue8g1/mEwLYYi2d950dJFpNYKW87jPS9/KM65JKMyxoU7tU2d4An1BuyqK0r9sopGq6Pn/zhill0iLaw==", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/layout-grid/-/layout-grid-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-5GqmT6oTZhUGWIb+CLD0ZNyDyTiJsr/rm9oRIi3+vCujACwxFkON9tzBlZohdtFS16nuzUusthN6Jt9UrJcN6Q==", "dependencies": { "tslib": "^2.1.0" } }, "node_modules/@material/line-ripple": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/line-ripple/-/line-ripple-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-evjZxCu4iodiKtW8N0xjY8ACRXm3sY+4rAmq3vV5BmHWAJ3BobjbFYslDMZQ+4mu3HmwMatbJehKxHegahitNg==", - "dependencies": { - "@material/animation": "15.0.0-canary.b994146f6.0", - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/line-ripple/-/line-ripple-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-8S30WXEuUdgDdBulzUDlPXD6qMzwCX9SxYb5mGDYLwl199cpSGdXHtGgEcCjokvnpLhdZhcT1Dsxeo1g2Evh5Q==", + "dependencies": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "node_modules/@material/linear-progress": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/linear-progress/-/linear-progress-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-jlXh+tIj+/o0Ks7fHdC/24fH6IXCAl2vF52U6NwT39ESrlwmlLhp3gtag5GSBHN5E7Z09nK871Yo1G/b1F+COg==", - "dependencies": { - "@material/animation": "15.0.0-canary.b994146f6.0", - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/progress-indicator": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/linear-progress/-/linear-progress-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-6EJpjrz6aoH2/gXLg9iMe0yF2C42hpQyZoHpmcgTLKeci85ktDvJIjwup8tnk8ULQyFiGiIrhXw2v2RSsiFjvQ==", + "dependencies": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/progress-indicator": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "node_modules/@material/list": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/list/-/list-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-kY/i6VvFBb/W3VvCPvWRMzWvu7mvNFJ+R8ijfawDoAXiv4fj42GO4iFyTcFXaUevEPKp791pN/09BMJQ6jYEvA==", - "dependencies": { - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/density": "15.0.0-canary.b994146f6.0", - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/ripple": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/shape": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/tokens": "15.0.0-canary.b994146f6.0", - "@material/typography": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/list/-/list-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-TQ1ppqiCMQj/P7bGD4edbIIv4goczZUoiUAaPq/feb1dflvrFMzYqJ7tQRRCyBL8nRhJoI2x99tk8Q2RXvlGUQ==", + "dependencies": { + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/density": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "node_modules/@material/menu": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/menu/-/menu-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-y6smNmLJ+U0DoXWbyqzW+VW/uWDuklhdGHc5MbZrTOhsKkhvoTVNMSOa+NFPU4gTwrplvUjaUvnIsQ0wygwD3g==", - "dependencies": { - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/elevation": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/list": "15.0.0-canary.b994146f6.0", - "@material/menu-surface": "15.0.0-canary.b994146f6.0", - "@material/ripple": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/shape": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/tokens": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/menu/-/menu-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-IlAh61xzrzxXs38QZlt74UYt8J431zGznSzDtB1Fqs6YFNd11QPKoiRXn1J2Qu/lUxbFV7i8NBKMCKtia0n6/Q==", + "dependencies": { + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/list": "15.0.0-canary.bc9ae6c9c.0", + "@material/menu-surface": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "node_modules/@material/menu-surface": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/menu-surface/-/menu-surface-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-StmM3lrRn1iMEZfq532jpMNppqyBBy68FbPurKEsHuP/3q+CscfnwjrS9ym+JcHqXKMHnQXbL/49ymffRGX2AQ==", - "dependencies": { - "@material/animation": "15.0.0-canary.b994146f6.0", - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/elevation": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/shape": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/menu-surface/-/menu-surface-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-dMtSPN+olTWE+08M5qe4ea1IZOhVryYqzK0Gyb2u1G75rSArUxCOB5rr6OC/ST3Mq3RS6zGuYo7srZt4534K9Q==", + "dependencies": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "node_modules/@material/notched-outline": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/notched-outline/-/notched-outline-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-UZxU8jXM2t/bk/CiO0K+TSPspuJRZIyrYlIS0gd+qq/u8Gi2DpALBlLAh9Jeu46IUg4YGlPsNWYfe8p3QAVyoA==", - "dependencies": { - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/floating-label": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/shape": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/notched-outline/-/notched-outline-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-WuurMg44xexkvLTBTnsO0A+qnzFjpcPdvgWBGstBepYozsvSF9zJGdb1x7Zv1MmqbpYh/Ohnuxtb/Y3jOh6irg==", + "dependencies": { + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/floating-label": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "node_modules/@material/progress-indicator": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/progress-indicator/-/progress-indicator-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-VT+mOQhohaM+pBX1rknbVOI6JCGKg9NiOHBoYljIvnexNeILE+mW9g6mtQ0ZCJPz0oMmiSAMLcuxMIcBXx84Xw==", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/progress-indicator/-/progress-indicator-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-uOnsvqw5F2fkeTnTl4MrYzjI7KCLmmLyZaM0cgLNuLsWVlddQE+SGMl28tENx7DUK3HebWq0FxCP8f25LuDD+w==", "dependencies": { "tslib": "^2.1.0" } }, "node_modules/@material/radio": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/radio/-/radio-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-U/RR2lVNWwEO2+kJtGz9XzvnOF0gAZn1krMY0z/eU9Wnl0OgPZbqQrxXMoVNv1pzKYSEwZQEGado/rv8qp7piA==", - "dependencies": { - "@material/animation": "15.0.0-canary.b994146f6.0", - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/density": "15.0.0-canary.b994146f6.0", - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/focus-ring": "15.0.0-canary.b994146f6.0", - "@material/ripple": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/touch-target": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/radio/-/radio-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-ehzOK+U1IxQN+OQjgD2lsnf1t7t7RAwQzeO6Czkiuid29ookYbQynWuLWk7NW8H8ohl7lnmfqTP1xSNkkL/F0g==", + "dependencies": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/density": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/focus-ring": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/touch-target": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "node_modules/@material/ripple": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/ripple/-/ripple-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-WzIbc8wYTzMOczqGXVCBPdNcv/73Ef8FwcQYsscGMaqCzgVsdpoqilTfsx7Ryyz6dQbyfmJqp7s+YpPujcezOA==", - "dependencies": { - "@material/animation": "15.0.0-canary.b994146f6.0", - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/ripple/-/ripple-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-JfLW+g3GMVDv4cruQ19+HUxpKVdWCldFlIPw1UYezz2h3WTNDy05S3uP2zUdXzZ01C3dkBFviv4nqZ0GCT16MA==", + "dependencies": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "node_modules/@material/rtl": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/rtl/-/rtl-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-H/W6BVn4Ygfkrf/FgSrNhbu1uY7PST2wlsjEYQt06EfAM0CDHEwSL1MwV4FmpQA/r40Q0PqoLN6moDrtCe5S8g==", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/rtl/-/rtl-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-SkKLNLFp5QtG7/JEFg9R92qq4MzTcZ5As6sWbH7rRg6ahTHoJEuqE+pOb9Vrtbj84k5gtX+vCYPvCILtSlr2uw==", "dependencies": { - "@material/theme": "15.0.0-canary.b994146f6.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "node_modules/@material/segmented-button": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/segmented-button/-/segmented-button-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-jd+f4BTnU0tghxBpAM/XdVmruDXSoQ88TYSFWbrhulS+/c/ooCZURWvVC4mHNej+QR/fODkx4adbqkBiwwCtMw==", - "dependencies": { - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/elevation": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/ripple": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/touch-target": "15.0.0-canary.b994146f6.0", - "@material/typography": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/segmented-button/-/segmented-button-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-YDwkCWP9l5mIZJ7pZJZ2hMDxfBlIGVJ+deNzr8O+Z7/xC5LGXbl4R5aPtUVHygvXAXxpf5096ZD+dSXzYzvWlw==", + "dependencies": { + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/touch-target": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "node_modules/@material/select": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/select/-/select-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-5thEQS+B17JSm3I8D+mqQe2G3ArVnXJALTEEE9FmMUKwKYkrsLplm3FYuEXERZGJnYeTRdkdmhYY/YeocfZoyA==", - "dependencies": { - "@material/animation": "15.0.0-canary.b994146f6.0", - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/density": "15.0.0-canary.b994146f6.0", - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/elevation": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/floating-label": "15.0.0-canary.b994146f6.0", - "@material/line-ripple": "15.0.0-canary.b994146f6.0", - "@material/list": "15.0.0-canary.b994146f6.0", - "@material/menu": "15.0.0-canary.b994146f6.0", - "@material/menu-surface": "15.0.0-canary.b994146f6.0", - "@material/notched-outline": "15.0.0-canary.b994146f6.0", - "@material/ripple": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/shape": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/tokens": "15.0.0-canary.b994146f6.0", - "@material/typography": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/select/-/select-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-unfOWVf7T0sixVG+3k3RTuATfzqvCF6QAzA6J9rlCh/Tq4HuIBNDdV4z19IVu4zwmgWYxY0iSvqWUvdJJYwakQ==", + "dependencies": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/density": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/floating-label": "15.0.0-canary.bc9ae6c9c.0", + "@material/line-ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/list": "15.0.0-canary.bc9ae6c9c.0", + "@material/menu": "15.0.0-canary.bc9ae6c9c.0", + "@material/menu-surface": "15.0.0-canary.bc9ae6c9c.0", + "@material/notched-outline": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "node_modules/@material/shape": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/shape/-/shape-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-sINM3gr3aLgdvqZVfqfXV5EB77owLLJjy+2NqchJ8ZPqucCJ+F/BsCBfLA2Wu3O4Sc9IpAEn/o1hzYm/CWAFAw==", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/shape/-/shape-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-Dsvr771ZKC46ODzoixLdGwlLEQLfxfLrtnRojXABoZf5G3o9KtJU+J+5Ld5aa960OAsCzzANuaub4iR88b1guA==", "dependencies": { - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "node_modules/@material/slider": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/slider/-/slider-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-dyT72+Kp//AEajJxDUVoMoizUjf2uggVMGXOaQ7FhpGHuf7LC3EyEjrrJ15efFzYgTjdJUU1YQkCwGmdt6CQsA==", - "dependencies": { - "@material/animation": "15.0.0-canary.b994146f6.0", - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/elevation": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/ripple": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/tokens": "15.0.0-canary.b994146f6.0", - "@material/typography": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/slider/-/slider-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-3AEu+7PwW4DSNLndue47dh2u7ga4hDJRYmuu7wnJCIWJBnLCkp6C92kNc4Rj5iQY2ftJio5aj1gqryluh5tlYg==", + "dependencies": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "node_modules/@material/snackbar": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/snackbar/-/snackbar-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-fEhPASJossScNpcrNYrrH8uU+rUf6+kw7/ZMrpUzzz1lVXliL28jTNEmU1nFpcDI4M2GXH+Z64f7vl2hiMDG8g==", - "dependencies": { - "@material/animation": "15.0.0-canary.b994146f6.0", - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/button": "15.0.0-canary.b994146f6.0", - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/elevation": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/icon-button": "15.0.0-canary.b994146f6.0", - "@material/ripple": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/shape": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/tokens": "15.0.0-canary.b994146f6.0", - "@material/typography": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/snackbar/-/snackbar-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-TwwQSYxfGK6mc03/rdDamycND6o+1p61WNd7ElZv1F1CLxB4ihRjbCoH7Qo+oVDaP8CTpjeclka+24RLhQq0mA==", + "dependencies": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/button": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/icon-button": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "node_modules/@material/switch": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/switch/-/switch-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-czCXTUa30ILIf1J3exiuSVIRcodGATHexd3eWDq4sfHo4iMh4rBMaIxcqkmnb2iwE/mMTNyVfoauijx2QiNKrA==", - "dependencies": { - "@material/animation": "15.0.0-canary.b994146f6.0", - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/density": "15.0.0-canary.b994146f6.0", - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/elevation": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/focus-ring": "15.0.0-canary.b994146f6.0", - "@material/ripple": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/shape": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/tokens": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/switch/-/switch-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-OjUjtT0kRz1ASAsOS+dNzwMwvsjmqy5edK57692qmrP6bL4GblFfBDoiNJ6t0AN4OaKcmL5Hy/xNrTdOZW7Qqw==", + "dependencies": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/density": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/focus-ring": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", "safevalues": "^0.3.4", "tslib": "^2.1.0" } }, "node_modules/@material/tab": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/tab/-/tab-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-ygswooiNdBNNDnQdbPX0nzDQu7oQlHo8vWZ0/xL4IPVEXabY5zCzsEbGNZw2u/syo56c/NHPyMsUmXDGRSXOvQ==", - "dependencies": { - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/elevation": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/focus-ring": "15.0.0-canary.b994146f6.0", - "@material/ripple": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/tab-indicator": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/tokens": "15.0.0-canary.b994146f6.0", - "@material/typography": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/tab/-/tab-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-s/L9otAwn/pZwVQZBRQJmPqYeNbjoEbzbjMpDQf/VBG/6dJ+aP03ilIBEkqo8NVnCoChqcdtVCoDNRtbU+yp6w==", + "dependencies": { + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/focus-ring": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/tab-indicator": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "node_modules/@material/tab-bar": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/tab-bar/-/tab-bar-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-F9NegACnFEWMu1pAAypV4Jd7qROeffkvEgVO28Xxk/CvzZxFz8kAjYJZ+rI6RUhPX3BhXzwsz/AlLwsJMT2tnA==", - "dependencies": { - "@material/animation": "15.0.0-canary.b994146f6.0", - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/density": "15.0.0-canary.b994146f6.0", - "@material/elevation": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/tab": "15.0.0-canary.b994146f6.0", - "@material/tab-indicator": "15.0.0-canary.b994146f6.0", - "@material/tab-scroller": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/tokens": "15.0.0-canary.b994146f6.0", - "@material/typography": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/tab-bar/-/tab-bar-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-Xmtq0wJGfu5k+zQeFeNsr4bUKv7L+feCmUp/gsapJ655LQKMXOUQZtSv9ZqWOfrCMy55hoF1CzGFV+oN3tyWWQ==", + "dependencies": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/density": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/tab": "15.0.0-canary.bc9ae6c9c.0", + "@material/tab-indicator": "15.0.0-canary.bc9ae6c9c.0", + "@material/tab-scroller": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "node_modules/@material/tab-indicator": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/tab-indicator/-/tab-indicator-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-8IH/DmwlZhQlw/2Y3aKrEvjEhZB+qbKUiyaij3BkTAexvyFeDBh5cLNjRpYkUJSGeSPhS6yu4SYzMHPmQEwQmA==", - "dependencies": { - "@material/animation": "15.0.0-canary.b994146f6.0", - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/tab-indicator/-/tab-indicator-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-despCJYi1GrDDq7F2hvLQkObHnSLZPPDxnOzU16zJ6FNYvIdszgfzn2HgAZ6pl5hLOexQ8cla6cAqjTDuaJBhQ==", + "dependencies": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "node_modules/@material/tab-scroller": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/tab-scroller/-/tab-scroller-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-1MeWkr62OICfTv8oqhIZe6jFo0dKeMlUfB+/WcgnpoeMBszCOSlx5tQ4pedxUkuR3I+Z7rsTfSN0LavgF8bATA==", - "dependencies": { - "@material/animation": "15.0.0-canary.b994146f6.0", - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/tab": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/tab-scroller/-/tab-scroller-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-QWHG/EWxirj4V9u2IHz+OSY9XCWrnNrPnNgEufxAJVUKV/A8ma1DYeFSQqxhX709R8wKGdycJksg0Flkl7Gq7w==", + "dependencies": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/tab": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "node_modules/@material/textfield": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/textfield/-/textfield-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-Kxb3DoJ5o8u3Y1gRMHKmWrDl1TirVxuf/UFrxPFiCE3J1SqiE2VQpakiD1emZwp+LSKtbRsQ/iILYLB/h7Wuvw==", - "dependencies": { - "@material/animation": "15.0.0-canary.b994146f6.0", - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/density": "15.0.0-canary.b994146f6.0", - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/floating-label": "15.0.0-canary.b994146f6.0", - "@material/line-ripple": "15.0.0-canary.b994146f6.0", - "@material/notched-outline": "15.0.0-canary.b994146f6.0", - "@material/ripple": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/shape": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/tokens": "15.0.0-canary.b994146f6.0", - "@material/typography": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/textfield/-/textfield-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-R3qRex9kCaZIAK8DuxPnVC42R0OaW7AB7fsFknDKeTeVQvRcbnV8E+iWSdqTiGdsi6QQHifX8idUrXw+O45zPw==", + "dependencies": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/density": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/floating-label": "15.0.0-canary.bc9ae6c9c.0", + "@material/line-ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/notched-outline": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "node_modules/@material/theme": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/theme/-/theme-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-5tsZ92dAeUcZ9g9CrIkqX/GYc0M5DIfsydtI1PAidaBzr1Uokuh4rTZVQZBv7gyglF0yDua59lkb0I6wI9vxXg==", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/theme/-/theme-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-CpUwXGE0dbhxQ45Hu9r9wbJtO/MAlv5ER4tBHA9tp/K+SU+lDgurBE2touFMg5INmdfVNtdumxb0nPPLaNQcUg==", "dependencies": { - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "node_modules/@material/tokens": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/tokens/-/tokens-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-jFqU7PtvGkrP8b8i2soCrYQInTrnZ1/rIPDi+Xm3sa/qSghCNwFrdJEqwcwtv1fPlJIOtzkIuVRYRmAP9rXQIQ==", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/tokens/-/tokens-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-nbEuGj05txWz6ZMUanpM47SaAD7soyjKILR+XwDell9Zg3bGhsnexCNXPEz2fD+YgomS+jM5XmIcaJJHg/H93Q==", "dependencies": { - "@material/elevation": "15.0.0-canary.b994146f6.0" + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0" } }, "node_modules/@material/tooltip": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/tooltip/-/tooltip-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-bVzydXGn3fauHJ8pkh32DsdyRJXleeFQ4t7jZ/rcRik+n4G1BvYiblfuu3Z/OCC0m3TJDyMdJhd+sLqRDqLUUg==", - "dependencies": { - "@material/animation": "15.0.0-canary.b994146f6.0", - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/button": "15.0.0-canary.b994146f6.0", - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/elevation": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/shape": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/tokens": "15.0.0-canary.b994146f6.0", - "@material/typography": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/tooltip/-/tooltip-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-UzuXp0b9NuWuYLYpPguxrjbJnCmT/Cco8CkjI/6JajxaeA3o2XEBbQfRMTq8PTafuBjCHTc0b0mQY7rtxUp1Gg==", + "dependencies": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/button": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", "safevalues": "^0.3.4", "tslib": "^2.1.0" } }, "node_modules/@material/top-app-bar": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/top-app-bar/-/top-app-bar-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-VHq0wX3OJE1TKvjO8Qtlu+rv5EGoqAhNLBcEjpUUGoqHH/gpd356FEuIqJId4pUh5jaWf8T4ZU9xVbQGMtntzw==", - "dependencies": { - "@material/animation": "15.0.0-canary.b994146f6.0", - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/elevation": "15.0.0-canary.b994146f6.0", - "@material/ripple": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/shape": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/typography": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/top-app-bar/-/top-app-bar-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-vJWjsvqtdSD5+yQ/9vgoBtBSCvPJ5uF/DVssv8Hdhgs1PYaAcODUi77kdi0+sy/TaWyOsTkQixqmwnFS16zesA==", + "dependencies": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "node_modules/@material/touch-target": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/touch-target/-/touch-target-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-X26Y9OWvIqYOHo+sC2VMvOoeQWlUR3/yb7uPdfq92Y44zlQ4Vexgq7nEUblEiXQ8Fj+d0T9rIhRh1y9PP3Z2dw==", - "dependencies": { - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/touch-target/-/touch-target-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-AqYh9fjt+tv4ZE0C6MeYHblS2H+XwLbDl2mtyrK0DOEnCVQk5/l5ImKDfhrUdFWHvS4a5nBM4AA+sa7KaroLoA==", + "dependencies": { + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "node_modules/@material/typography": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/typography/-/typography-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-sWU5W30WWqdw5P6bsRx9AbvMNcz/QvQg56Syr06V6nfgSztpeuo7TfPk2J+N0ArRALo1mUrkAPk66iWYQ2p/QA==", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/typography/-/typography-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-CKsG1zyv34AKPNyZC8olER2OdPII64iR2SzQjpqh1UUvmIFiMPk23LvQ1OnC5aCB14pOXzmVgvJt31r9eNdZ6Q==", "dependencies": { - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, @@ -4479,13 +4767,13 @@ } }, "node_modules/@schematics/angular": { - "version": "16.0.6", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-16.0.6.tgz", - "integrity": "sha512-8naIlMeY9p5iOZqc3D0reoN80xm/fQINrG8mqIOgIY6bDeqfFvMKfaozA3PbPLbZhl5Jyk7VfZnXb6ISN0KnxQ==", + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-16.2.0.tgz", + "integrity": "sha512-Ib0/ZCkjWt7a5p3209JVwEWwf41v03K3ylvlxLIEo1ZGijAZAlrBj4GrA5YQ+TmPm2hRyt+owss7x91/x+i0Gw==", "dev": true, "dependencies": { - "@angular-devkit/core": "16.0.6", - "@angular-devkit/schematics": "16.0.6", + "@angular-devkit/core": "16.2.0", + "@angular-devkit/schematics": "16.2.0", "jsonc-parser": "3.2.0" }, "engines": { @@ -4494,22 +4782,48 @@ "yarn": ">= 1.13.0" } }, + "node_modules/@sigstore/bundle": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-1.1.0.tgz", + "integrity": "sha512-PFutXEy0SmQxYI4texPw3dd2KewuNqv7OuK1ZFtY2fM754yhvG2KdgwIhRnoEE2uHdtdGNQ8s0lb94dW9sELog==", + "dev": true, + "dependencies": { + "@sigstore/protobuf-specs": "^0.2.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/@sigstore/protobuf-specs": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.1.0.tgz", - "integrity": "sha512-a31EnjuIDSX8IXBUib3cYLDRlPMU36AWX4xS8ysLaNu4ZzUesDiPt83pgrW2X1YLMe5L2HbDyaKK5BrL4cNKaQ==", + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.2.1.tgz", + "integrity": "sha512-XTWVxnWJu+c1oCshMLwnKvz8ZQJJDVOlciMfgpJBQbThVjKTCG8dwyhgLngBD2KN0ap9F/gOV8rFDEx8uh7R2A==", + "dev": true, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/@sigstore/sign": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@sigstore/sign/-/sign-1.0.0.tgz", + "integrity": "sha512-INxFVNQteLtcfGmcoldzV6Je0sbbfh9I16DM4yJPw3j5+TFP8X6uIiA18mvpEa9yyeycAKgPmOA3X9hVdVTPUA==", "dev": true, + "dependencies": { + "@sigstore/bundle": "^1.1.0", + "@sigstore/protobuf-specs": "^0.2.0", + "make-fetch-happen": "^11.0.1" + }, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/@sigstore/tuf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-1.0.2.tgz", - "integrity": "sha512-vjwcYePJzM01Ha6oWWZ9gNcdIgnzyFxfqfWzph483DPJTH8Tb7f7bQRRll3CYVkyH56j0AgcPAcl6Vg95DPF+Q==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-1.0.3.tgz", + "integrity": "sha512-2bRovzs0nJZFlCN3rXirE4gwxCn97JNjMmwpecqlbgV9WcxX7WRuIrgzx/X7Ib7MYRbyUTpBYE0s2x6AmZXnlg==", "dev": true, "dependencies": { - "@sigstore/protobuf-specs": "^0.1.0", + "@sigstore/protobuf-specs": "^0.2.0", "tuf-js": "^1.1.7" }, "engines": { @@ -4680,6 +4994,14 @@ "@types/send": "*" } }, + "node_modules/@types/fs-extra": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.2.tgz", + "integrity": "sha512-SvSrYXfWSc7R4eqnOzbQF4TZmfpNSM9FrSWLU3EUnWBuyZqNBOrv1B1JA3byUDPUl9z4Ab3jeZG2eDdySlgNMg==", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/http-errors": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.1.tgz", @@ -4716,8 +5038,7 @@ "node_modules/@types/node": { "version": "20.4.2", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.2.tgz", - "integrity": "sha512-Dd0BYtWgnWJKwO1jkmTrzofjK2QXXcai0dmtzvIBhcA+RsG5h8R3xlyta0kGOZRNfL9GuRtb1knmPEhQrePCEw==", - "dev": true + "integrity": "sha512-Dd0BYtWgnWJKwO1jkmTrzofjK2QXXcai0dmtzvIBhcA+RsG5h8R3xlyta0kGOZRNfL9GuRtb1knmPEhQrePCEw==" }, "node_modules/@types/qs": { "version": "6.9.7", @@ -4773,6 +5094,11 @@ "@types/node": "*" } }, + "node_modules/@types/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-+OpjSaq85gvlZAYINyzKpLeiFkSC4EsC6IIiT6v6TLSU5k5U83fHGj9Lel8oKEXM0HqgrMVCjXPDPVICtxF7EQ==" + }, "node_modules/@types/sockjs": { "version": "0.3.33", "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", @@ -4949,6 +5275,14 @@ "@xtuc/long": "4.2.2" } }, + "node_modules/@xmldom/xmldom": { + "version": "0.8.10", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", + "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/@xtuc/ieee754": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", @@ -5053,13 +5387,11 @@ } }, "node_modules/agentkeepalive": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.3.0.tgz", - "integrity": "sha512-7Epl1Blf4Sy37j4v9f9FjICCh4+KAQOyXgHEwlyBiAQLbhKdq/i2QQU3amQalS/wPhdPzDXPL5DMR5bkn+YeWg==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", + "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==", "dev": true, "dependencies": { - "debug": "^4.1.0", - "depd": "^2.0.0", "humanize-ms": "^1.2.1" }, "engines": { @@ -5164,7 +5496,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "engines": { "node": ">=8" } @@ -5228,12 +5559,28 @@ "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", "dev": true }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "engines": { + "node": ">=8" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "dev": true }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/autoprefixer": { "version": "10.4.14", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz", @@ -5352,14 +5699,12 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true, "funding": [ { "type": "github", @@ -5390,6 +5735,14 @@ "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", "dev": true }, + "node_modules/big-integer": { + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", + "engines": { + "node": ">=0.6" + } + }, "node_modules/big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", @@ -5476,6 +5829,17 @@ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", "dev": true }, + "node_modules/bplist-parser": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.3.2.tgz", + "integrity": "sha512-apC2+fspHGI3mMKj+dGevkGo/tCqVB8jMb6i+OX+E29p0Iposz07fABkRIfVUPNd5A5VbuOz1bZbnmkKLYF+wQ==", + "dependencies": { + "big-integer": "1.6.x" + }, + "engines": { + "node": ">= 5.10.0" + } + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -5554,6 +5918,14 @@ "ieee754": "^1.1.13" } }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "engines": { + "node": "*" + } + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -5770,7 +6142,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "dev": true, "engines": { "node": ">=10" } @@ -6462,7 +6833,6 @@ "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -6476,7 +6846,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "dependencies": { "isexe": "^2.0.0" }, @@ -6594,7 +6963,6 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, "dependencies": { "ms": "2.1.2" }, @@ -6644,7 +7012,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", - "dev": true, "engines": { "node": ">=8" } @@ -6828,11 +7195,26 @@ "integrity": "sha512-ux2LqN9JKRBDKXMT+78jtiBLPiXf+rLtYlsrOg5Qn7uv6Cbg7+9JyIalE3wcqkOdB2wPCUYNWAuL7suKRMHe9w==", "dev": true }, + "node_modules/elementtree": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/elementtree/-/elementtree-0.1.7.tgz", + "integrity": "sha512-wkgGT6kugeQk/P6VZ/f4T+4HB41BVgNBq5CDIZVbQ02nvTVqAiVTbskxxu3eA/X96lMlfYOwnLQpN2v5E1zDEg==", + "dependencies": { + "sax": "1.1.4" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/elementtree/node_modules/sax": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.1.4.tgz", + "integrity": "sha512-5f3k2PbGGp+YtKJjOItpg3P99IMD84E4HOvcfleTb5joCHNXYLsR9yWFPOYGgaeMPDubQILTCMdsFb2OMeOjtg==" + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "node_modules/emojis-list": { "version": "3.0.0", @@ -6940,7 +7322,6 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", - "dev": true, "engines": { "node": ">=6" } @@ -7395,6 +7776,14 @@ "node": ">=0.8.0" } }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dependencies": { + "pend": "~1.2.0" + } + }, "node_modules/figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -7631,8 +8020,7 @@ "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "node_modules/fsevents": { "version": "2.3.2", @@ -7812,8 +8200,7 @@ "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" }, "node_modules/handle-thing": { "version": "2.0.1", @@ -8283,13 +8670,12 @@ "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "node_modules/ini": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-4.0.0.tgz", - "integrity": "sha512-t0ikzf5qkSFqRl1e6ejKBe+Tk2bsQd8ivEkcisyGXsku2t8NvXZ1Y3RRz5vxrDgOrTBOi13CvGsVoI5wVpd7xg==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz", + "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==", "dev": true, "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -8464,7 +8850,6 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true, "bin": { "is-docker": "cli.js" }, @@ -8488,7 +8873,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, "engines": { "node": ">=8" } @@ -8593,7 +8977,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, "dependencies": { "is-docker": "^2.0.0" }, @@ -8622,8 +9005,7 @@ "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "node_modules/isobject": { "version": "3.0.1", @@ -9068,6 +9450,14 @@ "node": ">=0.10.0" } }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "engines": { + "node": ">=6" + } + }, "node_modules/klona": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", @@ -9583,7 +9973,6 @@ "version": "5.0.0", "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "dev": true, "engines": { "node": ">=8" } @@ -9619,12 +10008,12 @@ "dev": true }, "node_modules/minipass-fetch": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.3.tgz", - "integrity": "sha512-n5ITsTkDqYkYJZjcRWzZt9qnZKCT7nKCosJhHoj7S7zD+BP4jVbWs+odsniw5TA3E0sLomhTKOKjF86wf11PuQ==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.4.tgz", + "integrity": "sha512-jHAqnA728uUpIaFm7NWsCnqKT6UqZz7GcI/bDpPATuwYyKwJwW0remxSCxUlKiEty+eopHGa3oc8WxgQ1FFJqg==", "dev": true, "dependencies": { - "minipass": "^5.0.0", + "minipass": "^7.0.3", "minipass-sized": "^1.0.3", "minizlib": "^2.1.2" }, @@ -9635,6 +10024,15 @@ "encoding": "^0.1.13" } }, + "node_modules/minipass-fetch/node_modules/minipass": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.3.tgz", + "integrity": "sha512-LhbbwCfz3vsb12j/WkWQPZfKTsgqIe1Nf/ti1pKjYESGLHIVjWU96G9/ljLH4F9mWNVhlQOm0VySdAWzf05dpg==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/minipass-flush": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", @@ -9757,7 +10155,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "dev": true, "dependencies": { "minipass": "^3.0.0", "yallist": "^4.0.0" @@ -9770,7 +10167,6 @@ "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -9781,8 +10177,7 @@ "node_modules/minizlib/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/mkdirp": { "version": "0.5.6", @@ -9796,6 +10191,15 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/moment": { + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", + "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "peer": true, + "engines": { + "node": "*" + } + }, "node_modules/mrmime": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz", @@ -9808,8 +10212,7 @@ "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/multicast-dns": { "version": "7.2.5", @@ -9848,6 +10251,38 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/native-run": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/native-run/-/native-run-1.7.2.tgz", + "integrity": "sha512-2aahC8iXIO8BcvEukVMrYwL5sXurkuIGyQgfSGBto832W6ejV+cB5Ww+2/CRxmyozhbxARJ2OMpEGPV8sTqsrQ==", + "dependencies": { + "@ionic/utils-fs": "^3.1.6", + "@ionic/utils-terminal": "^2.3.3", + "bplist-parser": "^0.3.2", + "debug": "^4.3.4", + "elementtree": "^0.1.7", + "ini": "^3.0.1", + "plist": "^3.0.6", + "split2": "^4.1.0", + "through2": "^4.0.2", + "tslib": "^2.4.0", + "yauzl": "^2.10.0" + }, + "bin": { + "native-run": "bin/native-run" + }, + "engines": { + "node": ">=12.13.0" + } + }, + "node_modules/native-run/node_modules/ini": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ini/-/ini-3.0.1.tgz", + "integrity": "sha512-it4HyVAUTKBc6m8e1iXWvXSTdndF7HbdN713+kvLrymxTaU4AUBWrJ4vEooP+V7fexnVD3LKcBshjGGPefSMUQ==", + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, "node_modules/needle": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/needle/-/needle-3.2.0.tgz", @@ -10141,9 +10576,9 @@ } }, "node_modules/npm-install-checks": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-6.1.1.tgz", - "integrity": "sha512-dH3GmQL4vsPtld59cOn8uY0iOqRmqKvV+DLGwNXV/Q7MDgD2QfOADWd/mFXcIE5LVhYYGjA3baz6W9JneqnuCw==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-6.2.0.tgz", + "integrity": "sha512-744wat5wAAHsxa4590mWO0tJ8PKxR8ORZsH9wGpQc3nWTzozMAgBN/XyqYw7mg3yqLM8dLwEnwSfKMmXAjF69g==", "dev": true, "dependencies": { "semver": "^7.1.1" @@ -10333,7 +10768,6 @@ "version": "8.4.2", "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", - "dev": true, "dependencies": { "define-lazy-prop": "^2.0.0", "is-docker": "^2.1.1", @@ -10522,9 +10956,9 @@ } }, "node_modules/pacote": { - "version": "15.1.3", - "resolved": "https://registry.npmjs.org/pacote/-/pacote-15.1.3.tgz", - "integrity": "sha512-aRts8cZqxiJVDitmAh+3z+FxuO3tLNWEmwDRPEpDDiZJaRz06clP4XX112ynMT5uF0QNoMPajBBHnaStUEPJXA==", + "version": "15.2.0", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-15.2.0.tgz", + "integrity": "sha512-rJVZeIwHTUta23sIZgEIM62WYwbmGbThdbnkt81ravBplQv+HjyroqnLRNH2+sLJHcGZmLRmhPwACqhfTcOmnA==", "dev": true, "dependencies": { "@npmcli/git": "^4.0.0", @@ -10673,7 +11107,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "engines": { "node": ">=8" } @@ -10688,7 +11121,6 @@ "version": "1.10.1", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", - "dev": true, "dependencies": { "lru-cache": "^9.1.1 || ^10.0.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" @@ -10704,7 +11136,6 @@ "version": "10.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.0.tgz", "integrity": "sha512-svTf/fzsKHffP42sujkO/Rjs37BCIsQVRCeNYIm9WN8rgT7ffoUnRtZCqU+6BqcSBdv8gwJeTz8knJpgACeQMw==", - "dev": true, "engines": { "node": "14 || >=16.14" } @@ -10724,6 +11155,11 @@ "node": ">=8" } }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==" + }, "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -10778,6 +11214,19 @@ "node": ">=8" } }, + "node_modules/plist": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", + "integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==", + "dependencies": { + "@xmldom/xmldom": "^0.8.8", + "base64-js": "^1.5.1", + "xmlbuilder": "^15.1.1" + }, + "engines": { + "node": ">=10.4.0" + } + }, "node_modules/postcss": { "version": "8.4.24", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.24.tgz", @@ -10995,6 +11444,26 @@ "node": ">=10" } }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/prompts/node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", + "engines": { + "node": ">=6" + } + }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -11188,7 +11657,6 @@ "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -11495,7 +11963,6 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true, "funding": [ { "type": "github", @@ -11580,9 +12047,7 @@ "node_modules/sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true, - "optional": true + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, "node_modules/schema-utils": { "version": "4.2.0", @@ -11622,10 +12087,9 @@ } }, "node_modules/semver": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.4.0.tgz", - "integrity": "sha512-RgOxM8Mw+7Zus0+zcLEUn8+JfoLpj/huFTItQy2hsM4khuC1HYRDp0cU482Ewn/Fcy6bCjufD8vAj7voC66KQw==", - "dev": true, + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -11640,7 +12104,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -11651,8 +12114,7 @@ "node_modules/semver/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/send": { "version": "0.18.0", @@ -11841,7 +12303,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -11853,7 +12314,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "engines": { "node": ">=8" } @@ -11884,17 +12344,18 @@ "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, "node_modules/sigstore": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/sigstore/-/sigstore-1.7.0.tgz", - "integrity": "sha512-KP7QULhWdlu3hlp+jw2EvgWKlOGOY9McLj/jrchLjHNlNPK0KWIwF919cbmOp6QiKXLmPijR2qH/5KYWlbtG9Q==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/sigstore/-/sigstore-1.9.0.tgz", + "integrity": "sha512-0Zjz0oe37d08VeOtBIuB6cRriqXse2e8w+7yIy2XSXjshRKxbc2KkhXjL229jXSxEm7UbcjS76wcJDGQddVI9A==", "dev": true, "dependencies": { - "@sigstore/protobuf-specs": "^0.1.0", - "@sigstore/tuf": "^1.0.1", + "@sigstore/bundle": "^1.1.0", + "@sigstore/protobuf-specs": "^0.2.0", + "@sigstore/sign": "^1.0.0", + "@sigstore/tuf": "^1.0.3", "make-fetch-happen": "^11.0.1" }, "bin": { @@ -11904,6 +12365,11 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" + }, "node_modules/slash": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", @@ -11916,6 +12382,52 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/slice-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, "node_modules/smart-buffer": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", @@ -12143,6 +12655,14 @@ "wbuf": "^1.7.3" } }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "engines": { + "node": ">= 10.x" + } + }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -12188,7 +12708,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, "dependencies": { "safe-buffer": "~5.2.0" } @@ -12197,7 +12716,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -12226,7 +12744,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -12302,7 +12819,6 @@ "version": "6.1.15", "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.15.tgz", "integrity": "sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A==", - "dev": true, "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", @@ -12319,7 +12835,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dev": true, "dependencies": { "minipass": "^3.0.0" }, @@ -12331,7 +12846,6 @@ "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -12343,7 +12857,6 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, "bin": { "mkdirp": "bin/cmd.js" }, @@ -12354,8 +12867,7 @@ "node_modules/tar/node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/terser": { "version": "5.17.7", @@ -12484,6 +12996,14 @@ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "dev": true }, + "node_modules/through2": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "dependencies": { + "readable-stream": "3" + } + }, "node_modules/thunky": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", @@ -12542,7 +13062,6 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", - "dev": true, "bin": { "tree-kill": "cli.js" } @@ -12723,6 +13242,14 @@ "node": ">= 0.8" } }, + "node_modules/untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "engines": { + "node": ">=8" + } + }, "node_modules/update-browserslist-db": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", @@ -12765,8 +13292,7 @@ "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "node_modules/utils-merge": { "version": "1.0.1", @@ -13251,7 +13777,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -13319,7 +13844,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -13334,7 +13858,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "dependencies": { "color-name": "~1.1.4" }, @@ -13345,8 +13868,7 @@ "node_modules/wrap-ansi/node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, "node_modules/wrappy": { "version": "1.0.2", @@ -13375,6 +13897,34 @@ } } }, + "node_modules/xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xml2js/node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/xmlbuilder": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", + "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", + "engines": { + "node": ">=8.0" + } + }, "node_modules/xxhashjs": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/xxhashjs/-/xxhashjs-0.2.2.tgz", @@ -13426,6 +13976,15 @@ "node": ">=12" } }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, "node_modules/zone.js": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.13.1.tgz", @@ -13447,12 +14006,12 @@ } }, "@angular-devkit/architect": { - "version": "0.1600.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1600.6.tgz", - "integrity": "sha512-Mk/pRujuer5qRMrgC7DPwLQ88wTAEKhbs0yJ/1prm4cx+VkxX9MMf6Y4AHKRmduKmFmd2LmX21/ACiU65acH8w==", + "version": "0.1602.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1602.0.tgz", + "integrity": "sha512-ZRmUTBeD+uGr605eOHnsovEn6f1mOBI+kxP64DRvagNweX5TN04s3iyQ8jmLSAHQD9ush31LFxv3dVNxv3ceXQ==", "dev": true, "requires": { - "@angular-devkit/core": "16.0.6", + "@angular-devkit/core": "16.2.0", "rxjs": "7.8.1" } }, @@ -13641,9 +14200,9 @@ } }, "@angular-devkit/core": { - "version": "16.0.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-16.0.6.tgz", - "integrity": "sha512-pHbDUwXDMTWTnX/vafkFnzvYDQD8lz+w8FvMQE23Q/vN6/Q0BRf0PWTAGla6Wt+E4HaqqrbQS5P0YBwS4te2Pw==", + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-16.2.0.tgz", + "integrity": "sha512-l1k6Rqm3YM16BEn3CWyQKrk9xfu+2ux7Bw3oS+h1TO4/RoxO2PgHj8LLRh/WNrYVarhaqO7QZ5ePBkXNMkzJ1g==", "dev": true, "requires": { "ajv": "8.12.0", @@ -13654,16 +14213,27 @@ } }, "@angular-devkit/schematics": { - "version": "16.0.6", - "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-16.0.6.tgz", - "integrity": "sha512-Ipd3uEPgR0qz9HYQvY3RpWHO1DH34mQ6AShKiBypCCd/iwJPcJLKUVon2wYEfKlspgg9N8qWIuoMVHZG0Vwqgg==", + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/@angular-devkit/schematics/-/schematics-16.2.0.tgz", + "integrity": "sha512-QMDJXPE0+YQJ9Ap3MMzb0v7rx6ZbBEokmHgpdIjN3eILYmbAdsSGE8HTV8NjS9nKmcyE9OGzFCMb7PFrDTlTAw==", "dev": true, "requires": { - "@angular-devkit/core": "16.0.6", + "@angular-devkit/core": "16.2.0", "jsonc-parser": "3.2.0", - "magic-string": "0.30.0", + "magic-string": "0.30.1", "ora": "5.4.1", "rxjs": "7.8.1" + }, + "dependencies": { + "magic-string": { + "version": "0.30.1", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.1.tgz", + "integrity": "sha512-mbVKXPmS0z0G4XqFDCTllmDQ6coZzn94aMlb0o/A4HEHJCKcanlDZwYJgwnkmgD3jyWhUgj9VsPrfd972yPffA==", + "dev": true, + "requires": { + "@jridgewell/sourcemap-codec": "^1.4.15" + } + } } }, "@angular/animations": { @@ -13675,36 +14245,36 @@ } }, "@angular/cdk": { - "version": "16.1.5", - "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-16.1.5.tgz", - "integrity": "sha512-8wjYhLwW9bWicBYSdDXuN71SBP7NbJmXs+XiWiRkaFUkVDeU9z8Qkitogl+qqsSXvsOmi+12MowrbJ3tPizaLw==", + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/@angular/cdk/-/cdk-16.2.6.tgz", + "integrity": "sha512-vSaPs69xutbxc6IbZz4I5fMzZhlypsMg5JKKNAufmyYNNHQYgSQytpUd1/RxHhPF/JoEvj/J8QjauRriZFN+SA==", "requires": { "parse5": "^7.1.2", "tslib": "^2.3.0" } }, "@angular/cli": { - "version": "16.0.6", - "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-16.0.6.tgz", - "integrity": "sha512-um7oOWSu9SIzvwqJ5Aeqcki5/qj4yb6QKi8RkHDWpOdrg1tJfX/BnIzUa4jiCXIwYRIz+PjYJb8W5216wS+7Gg==", + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/@angular/cli/-/cli-16.2.0.tgz", + "integrity": "sha512-xT8vJOyw6Rc2364XDW2jHagLgKu7342ktd/lt+c0u6R+AB2XVFMePR7VceLohX9N/vRUsbQ0nVSZr+ru/hA+HA==", "dev": true, "requires": { - "@angular-devkit/architect": "0.1600.6", - "@angular-devkit/core": "16.0.6", - "@angular-devkit/schematics": "16.0.6", - "@schematics/angular": "16.0.6", + "@angular-devkit/architect": "0.1602.0", + "@angular-devkit/core": "16.2.0", + "@angular-devkit/schematics": "16.2.0", + "@schematics/angular": "16.2.0", "@yarnpkg/lockfile": "1.1.0", "ansi-colors": "4.1.3", - "ini": "4.0.0", + "ini": "4.1.1", "inquirer": "8.2.4", "jsonc-parser": "3.2.0", "npm-package-arg": "10.1.0", "npm-pick-manifest": "8.0.1", "open": "8.4.2", "ora": "5.4.1", - "pacote": "15.1.3", + "pacote": "15.2.0", "resolve": "1.22.2", - "semver": "7.4.0", + "semver": "7.5.4", "symbol-observable": "4.0.0", "yargs": "17.7.2" } @@ -13758,57 +14328,65 @@ } }, "@angular/material": { - "version": "16.1.5", - "resolved": "https://registry.npmjs.org/@angular/material/-/material-16.1.5.tgz", - "integrity": "sha512-l11mH/WWBmfiBhrf4/0hCihhLxK4Ldu7+fP8zucHO3X2TiLlpsgJZpcYwJkZf0Ai0rDqIzqCVnks7L9jiuTGCQ==", - "requires": { - "@material/animation": "15.0.0-canary.b994146f6.0", - "@material/auto-init": "15.0.0-canary.b994146f6.0", - "@material/banner": "15.0.0-canary.b994146f6.0", - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/button": "15.0.0-canary.b994146f6.0", - "@material/card": "15.0.0-canary.b994146f6.0", - "@material/checkbox": "15.0.0-canary.b994146f6.0", - "@material/chips": "15.0.0-canary.b994146f6.0", - "@material/circular-progress": "15.0.0-canary.b994146f6.0", - "@material/data-table": "15.0.0-canary.b994146f6.0", - "@material/density": "15.0.0-canary.b994146f6.0", - "@material/dialog": "15.0.0-canary.b994146f6.0", - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/drawer": "15.0.0-canary.b994146f6.0", - "@material/elevation": "15.0.0-canary.b994146f6.0", - "@material/fab": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/floating-label": "15.0.0-canary.b994146f6.0", - "@material/form-field": "15.0.0-canary.b994146f6.0", - "@material/icon-button": "15.0.0-canary.b994146f6.0", - "@material/image-list": "15.0.0-canary.b994146f6.0", - "@material/layout-grid": "15.0.0-canary.b994146f6.0", - "@material/line-ripple": "15.0.0-canary.b994146f6.0", - "@material/linear-progress": "15.0.0-canary.b994146f6.0", - "@material/list": "15.0.0-canary.b994146f6.0", - "@material/menu": "15.0.0-canary.b994146f6.0", - "@material/menu-surface": "15.0.0-canary.b994146f6.0", - "@material/notched-outline": "15.0.0-canary.b994146f6.0", - "@material/radio": "15.0.0-canary.b994146f6.0", - "@material/ripple": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/segmented-button": "15.0.0-canary.b994146f6.0", - "@material/select": "15.0.0-canary.b994146f6.0", - "@material/shape": "15.0.0-canary.b994146f6.0", - "@material/slider": "15.0.0-canary.b994146f6.0", - "@material/snackbar": "15.0.0-canary.b994146f6.0", - "@material/switch": "15.0.0-canary.b994146f6.0", - "@material/tab": "15.0.0-canary.b994146f6.0", - "@material/tab-bar": "15.0.0-canary.b994146f6.0", - "@material/tab-indicator": "15.0.0-canary.b994146f6.0", - "@material/tab-scroller": "15.0.0-canary.b994146f6.0", - "@material/textfield": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/tooltip": "15.0.0-canary.b994146f6.0", - "@material/top-app-bar": "15.0.0-canary.b994146f6.0", - "@material/touch-target": "15.0.0-canary.b994146f6.0", - "@material/typography": "15.0.0-canary.b994146f6.0", + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/@angular/material/-/material-16.2.6.tgz", + "integrity": "sha512-JFP12dLrsKwrQ4zZtSRJarYosnxikxLD2M9hfUtHVxgTJr7rdSQ8eE7G2l2zPALSUt+d44MWgQ79xu6inuvEOw==", + "requires": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/auto-init": "15.0.0-canary.bc9ae6c9c.0", + "@material/banner": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/button": "15.0.0-canary.bc9ae6c9c.0", + "@material/card": "15.0.0-canary.bc9ae6c9c.0", + "@material/checkbox": "15.0.0-canary.bc9ae6c9c.0", + "@material/chips": "15.0.0-canary.bc9ae6c9c.0", + "@material/circular-progress": "15.0.0-canary.bc9ae6c9c.0", + "@material/data-table": "15.0.0-canary.bc9ae6c9c.0", + "@material/density": "15.0.0-canary.bc9ae6c9c.0", + "@material/dialog": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/drawer": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/fab": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/floating-label": "15.0.0-canary.bc9ae6c9c.0", + "@material/form-field": "15.0.0-canary.bc9ae6c9c.0", + "@material/icon-button": "15.0.0-canary.bc9ae6c9c.0", + "@material/image-list": "15.0.0-canary.bc9ae6c9c.0", + "@material/layout-grid": "15.0.0-canary.bc9ae6c9c.0", + "@material/line-ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/linear-progress": "15.0.0-canary.bc9ae6c9c.0", + "@material/list": "15.0.0-canary.bc9ae6c9c.0", + "@material/menu": "15.0.0-canary.bc9ae6c9c.0", + "@material/menu-surface": "15.0.0-canary.bc9ae6c9c.0", + "@material/notched-outline": "15.0.0-canary.bc9ae6c9c.0", + "@material/radio": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/segmented-button": "15.0.0-canary.bc9ae6c9c.0", + "@material/select": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/slider": "15.0.0-canary.bc9ae6c9c.0", + "@material/snackbar": "15.0.0-canary.bc9ae6c9c.0", + "@material/switch": "15.0.0-canary.bc9ae6c9c.0", + "@material/tab": "15.0.0-canary.bc9ae6c9c.0", + "@material/tab-bar": "15.0.0-canary.bc9ae6c9c.0", + "@material/tab-indicator": "15.0.0-canary.bc9ae6c9c.0", + "@material/tab-scroller": "15.0.0-canary.bc9ae6c9c.0", + "@material/textfield": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tooltip": "15.0.0-canary.bc9ae6c9c.0", + "@material/top-app-bar": "15.0.0-canary.bc9ae6c9c.0", + "@material/touch-target": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", + "tslib": "^2.3.0" + } + }, + "@angular/material-moment-adapter": { + "version": "16.2.6", + "resolved": "https://registry.npmjs.org/@angular/material-moment-adapter/-/material-moment-adapter-16.2.6.tgz", + "integrity": "sha512-lV+eUp1PVHU2BU+RMAxHqQTwUelOA9go/0t/hFGDVhF9eTGZyzl6CnWgmksPjQCnZTrW3Pb1BhqsizcjLfh1Gw==", + "requires": { "tslib": "^2.3.0" } }, @@ -15055,6 +15633,85 @@ "to-fast-properties": "^2.0.0" } }, + "@capacitor/cli": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@capacitor/cli/-/cli-5.3.0.tgz", + "integrity": "sha512-ku23HPqUHUnSgo/SyEWxVviEAxb4ieWvAVMI3KfrrBoinAhTOvNSZwT346rIpxZ9Xj3Qp41UjdIz0ME+DYwhfA==", + "requires": { + "@ionic/cli-framework-output": "^2.2.5", + "@ionic/utils-fs": "^3.1.6", + "@ionic/utils-subprocess": "^2.1.11", + "@ionic/utils-terminal": "^2.3.3", + "commander": "^9.3.0", + "debug": "^4.3.4", + "env-paths": "^2.2.0", + "kleur": "^4.1.4", + "native-run": "^1.7.2", + "open": "^8.4.0", + "plist": "^3.0.5", + "prompts": "^2.4.2", + "rimraf": "^4.4.1", + "semver": "^7.3.7", + "tar": "^6.1.11", + "tslib": "^2.4.0", + "xml2js": "^0.5.0" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "requires": { + "balanced-match": "^1.0.0" + } + }, + "commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==" + }, + "glob": { + "version": "9.3.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-9.3.5.tgz", + "integrity": "sha512-e1LleDykUz2Iu+MTYdkSsuWX8lvAjAcs0Xef0lNIu0S2wOAzuTxCJtcd9S3cijlwYF18EsU3rzb8jPVobxDh9Q==", + "requires": { + "fs.realpath": "^1.0.0", + "minimatch": "^8.0.2", + "minipass": "^4.2.4", + "path-scurry": "^1.6.1" + } + }, + "minimatch": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-8.0.4.tgz", + "integrity": "sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==", + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "minipass": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz", + "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==" + }, + "rimraf": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-4.4.1.tgz", + "integrity": "sha512-Gk8NlF062+T9CqNGn6h4tls3k6T1+/nXdOcSZVikNVtlRdYpA7wRJJMoXmuvOnLW844rPjdQ7JgXCYM6PPC/og==", + "requires": { + "glob": "^9.2.0" + } + } + } + }, + "@capacitor/core": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/@capacitor/core/-/core-5.3.0.tgz", + "integrity": "sha512-mvhh1yJtcUTZ0hUUriBKKpxq47hn75bjxH3tYPRgAFu1z3gowCg+OtG4Rce3W5gr5fSfCjQgOSL0Vp7k9hPUWw==", + "requires": { + "tslib": "^2.1.0" + } + }, "@colors/colors": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", @@ -15221,6 +15878,125 @@ "dev": true, "optional": true }, + "@ionic/cli-framework-output": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/@ionic/cli-framework-output/-/cli-framework-output-2.2.6.tgz", + "integrity": "sha512-YLPRwnk5Lw0XQ9pKWG+p2KoR5HjMBigZ6yv+/XtL3TGOnCS1+oAz56ABbAORCjTWhSJQisr8APNFiELAecY6QA==", + "requires": { + "@ionic/utils-terminal": "2.3.4", + "debug": "^4.0.0", + "tslib": "^2.0.1" + } + }, + "@ionic/utils-array": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@ionic/utils-array/-/utils-array-2.1.6.tgz", + "integrity": "sha512-0JZ1Zkp3wURnv8oq6Qt7fMPo5MpjbLoUoa9Bu2Q4PJuSDWM8H8gwF3dQO7VTeUj3/0o1IB1wGkFWZZYgUXZMUg==", + "requires": { + "debug": "^4.0.0", + "tslib": "^2.0.1" + } + }, + "@ionic/utils-fs": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@ionic/utils-fs/-/utils-fs-3.1.7.tgz", + "integrity": "sha512-2EknRvMVfhnyhL1VhFkSLa5gOcycK91VnjfrTB0kbqkTFCOXyXgVLI5whzq7SLrgD9t1aqos3lMMQyVzaQ5gVA==", + "requires": { + "@types/fs-extra": "^8.0.0", + "debug": "^4.0.0", + "fs-extra": "^9.0.0", + "tslib": "^2.0.1" + }, + "dependencies": { + "fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "universalify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", + "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==" + } + } + }, + "@ionic/utils-object": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@ionic/utils-object/-/utils-object-2.1.6.tgz", + "integrity": "sha512-vCl7sl6JjBHFw99CuAqHljYJpcE88YaH2ZW4ELiC/Zwxl5tiwn4kbdP/gxi2OT3MQb1vOtgAmSNRtusvgxI8ww==", + "requires": { + "debug": "^4.0.0", + "tslib": "^2.0.1" + } + }, + "@ionic/utils-process": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/@ionic/utils-process/-/utils-process-2.1.11.tgz", + "integrity": "sha512-Uavxn+x8j3rDlZEk1X7YnaN6wCgbCwYQOeIjv/m94i1dzslqWhqIHEqxEyeE8HsT5Negboagg7GtQiABy+BLbA==", + "requires": { + "@ionic/utils-object": "2.1.6", + "@ionic/utils-terminal": "2.3.4", + "debug": "^4.0.0", + "signal-exit": "^3.0.3", + "tree-kill": "^1.2.2", + "tslib": "^2.0.1" + } + }, + "@ionic/utils-stream": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/@ionic/utils-stream/-/utils-stream-3.1.6.tgz", + "integrity": "sha512-4+Kitey1lTA1yGtnigeYNhV/0tggI3lWBMjC7tBs1K9GXa/q7q4CtOISppdh8QgtOhrhAXS2Igp8rbko/Cj+lA==", + "requires": { + "debug": "^4.0.0", + "tslib": "^2.0.1" + } + }, + "@ionic/utils-subprocess": { + "version": "2.1.12", + "resolved": "https://registry.npmjs.org/@ionic/utils-subprocess/-/utils-subprocess-2.1.12.tgz", + "integrity": "sha512-N05Y+dIXBHofKWJTheCMzVqmgY9wFmZcRv/LdNnfXaaA/mxLTyGxQYeig8fvQXTtDafb/siZXcrTkmQ+y6n3Yg==", + "requires": { + "@ionic/utils-array": "2.1.6", + "@ionic/utils-fs": "3.1.7", + "@ionic/utils-process": "2.1.11", + "@ionic/utils-stream": "3.1.6", + "@ionic/utils-terminal": "2.3.4", + "cross-spawn": "^7.0.3", + "debug": "^4.0.0", + "tslib": "^2.0.1" + } + }, + "@ionic/utils-terminal": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@ionic/utils-terminal/-/utils-terminal-2.3.4.tgz", + "integrity": "sha512-cEiMFl3jklE0sW60r8JHH3ijFTwh/jkdEKWbylSyExQwZ8pPuwoXz7gpkWoJRLuoRHHSvg+wzNYyPJazIHfoJA==", + "requires": { + "@types/slice-ansi": "^4.0.0", + "debug": "^4.0.0", + "signal-exit": "^3.0.3", + "slice-ansi": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0", + "tslib": "^2.0.1", + "untildify": "^4.0.0", + "wrap-ansi": "^7.0.0" + } + }, "@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -15375,754 +16151,754 @@ "dev": true }, "@material/animation": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/animation/-/animation-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-kqqzG54tabYJ5VsBur5k1bqCFQCEpaW3hmLRMiSVVxRY7XgTt7qkuOOz48gs+MPqR6P8VIi6gFpuscV1+DWDhw==", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/animation/-/animation-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-leRf+BcZTfC/iSigLXnYgcHAGvFVQveoJT5+2PIRdyPI/bIG7hhciRgacHRsCKC0sGya81dDblLgdkjSUemYLw==", "requires": { "tslib": "^2.1.0" } }, "@material/auto-init": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/auto-init/-/auto-init-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-8nLe/XeueJg5yyYx5e4UxWQXpTDyUhibKfyroGwnRKc8pdpOCOulHSOj/fIVGJAIbxkEJoebwMadWUNCjUhc9A==", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/auto-init/-/auto-init-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-uxzDq7q3c0Bu1pAsMugc1Ik9ftQYQqZY+5e2ybNplT8gTImJhNt4M2mMiMHbMANk2l3UgICmUyRSomgPBWCPIA==", "requires": { - "@material/base": "15.0.0-canary.b994146f6.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "@material/banner": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/banner/-/banner-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-gJ4/VdP4dJgHP72Kdjy2f/UjHB45J4CuxoGvI0NIQYUjOSsr4kQiQHsjVgyEPZR/5wa7kBhM7/0mJ+zF7Ghv2A==", - "requires": { - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/button": "15.0.0-canary.b994146f6.0", - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/elevation": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/ripple": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/shape": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/tokens": "15.0.0-canary.b994146f6.0", - "@material/typography": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/banner/-/banner-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-SHeVoidCUFVhXANN6MNWxK9SZoTSgpIP8GZB7kAl52BywLxtV+FirTtLXkg/8RUkxZRyRWl7HvQ0ZFZa7QQAyA==", + "requires": { + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/button": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "@material/base": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/base/-/base-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-rW2upYD5YjRFBL6DzYn3SCRhtvpEDkwplDS810e3vt71uLMRyqXyw4OQJH+Nab/t+32TFDtKNUphXIzwICXGDQ==", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/base/-/base-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-Fc3vGuOf+duGo22HTRP6dHdc+MUe0VqQfWOuKrn/wXKD62m0QQR2TqJd3rRhCumH557T5QUyheW943M3E+IGfg==", "requires": { "tslib": "^2.1.0" } }, "@material/button": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/button/-/button-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-SMyqtsvJuCqpXBz2JgciuR6wddNJSGpTXUFxmLbGluBy5/hHm06JWlOFcUOxGDv46OdRGGrRfkg6A9JtvtsJsw==", - "requires": { - "@material/density": "15.0.0-canary.b994146f6.0", - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/elevation": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/focus-ring": "15.0.0-canary.b994146f6.0", - "@material/ripple": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/shape": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/tokens": "15.0.0-canary.b994146f6.0", - "@material/touch-target": "15.0.0-canary.b994146f6.0", - "@material/typography": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/button/-/button-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-3AQgwrPZCTWHDJvwgKq7Cj+BurQ4wTjDdGL+FEnIGUAjJDskwi1yzx5tW2Wf/NxIi7IoPFyOY3UB41jwMiOrnw==", + "requires": { + "@material/density": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/focus-ring": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/touch-target": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "@material/card": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/card/-/card-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-WSggGon91HcDhJyatnYLFkoM9glkkeJjyjFDWrcJkwN1rdrPJU+GH+PNjvmArz5hGv9WkmjDjhOdAuPnL4Mb7g==", - "requires": { - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/elevation": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/ripple": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/shape": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/tokens": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/card/-/card-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-nPlhiWvbLmooTnBmV5gmzB0eLWSgLKsSRBYAbIBmO76Okgz1y+fQNLag+lpm/TDaHVsn5fmQJH8e0zIg0rYsQA==", + "requires": { + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "@material/checkbox": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/checkbox/-/checkbox-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-pulRiwG9S/dS6WBG+GteODBltddFiL0Sb7HAqdzF2BTKNKv25q1ZIR3ftoEa09TNeWM88AOzTJ4aBHiADfJn2w==", - "requires": { - "@material/animation": "15.0.0-canary.b994146f6.0", - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/density": "15.0.0-canary.b994146f6.0", - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/focus-ring": "15.0.0-canary.b994146f6.0", - "@material/ripple": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/touch-target": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/checkbox/-/checkbox-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-4tpNnO1L0IppoMF3oeQn8F17t2n0WHB0D7mdJK9rhrujen/fLbekkIC82APB3fdGtLGg3qeNqDqPsJm1YnmrwA==", + "requires": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/density": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/focus-ring": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/touch-target": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "@material/chips": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/chips/-/chips-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-3yJPj7x+eKLA4LMKG7aTWI+itAnKRVGOcniuR6aiXVy0OKr5asNuWNeZc9J0/VErjjxF3tdybDzDSPo01qPy9w==", - "requires": { - "@material/animation": "15.0.0-canary.b994146f6.0", - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/checkbox": "15.0.0-canary.b994146f6.0", - "@material/density": "15.0.0-canary.b994146f6.0", - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/elevation": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/focus-ring": "15.0.0-canary.b994146f6.0", - "@material/ripple": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/shape": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/tokens": "15.0.0-canary.b994146f6.0", - "@material/touch-target": "15.0.0-canary.b994146f6.0", - "@material/typography": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/chips/-/chips-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-fqHKvE5bSWK0bXVkf57MWxZtytGqYBZvvHIOs4JI9HPHEhaJy4CpSw562BEtbm3yFxxALoQknvPW2KYzvADnmA==", + "requires": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/checkbox": "15.0.0-canary.bc9ae6c9c.0", + "@material/density": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/focus-ring": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/touch-target": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", "safevalues": "^0.3.4", "tslib": "^2.1.0" } }, "@material/circular-progress": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/circular-progress/-/circular-progress-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-6YUvGXdtZKJoE7AuovR4xk1aiWp/EDZ6j2U3TOeynd1assQQCg5XT4abqAoHtpJrRPaCFgUAp836HyiDVVuYug==", - "requires": { - "@material/animation": "15.0.0-canary.b994146f6.0", - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/progress-indicator": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/circular-progress/-/circular-progress-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-Lxe8BGAxQwCQqrLhrYrIP0Uok10h7aYS3RBXP41ph+5GmwJd5zdyE2t93qm2dyThvU6qKuXw9726Dtq/N+wvZQ==", + "requires": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/progress-indicator": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "@material/data-table": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/data-table/-/data-table-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-v4hIduIe/wzyibuL/RPM/ErYrt8XpB7fxyQqtV+0JsMpFa8E81QYyvMCS9EJj9m4YdkrQnZgA+vXQlOkhWvmdQ==", - "requires": { - "@material/animation": "15.0.0-canary.b994146f6.0", - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/checkbox": "15.0.0-canary.b994146f6.0", - "@material/density": "15.0.0-canary.b994146f6.0", - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/elevation": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/icon-button": "15.0.0-canary.b994146f6.0", - "@material/linear-progress": "15.0.0-canary.b994146f6.0", - "@material/list": "15.0.0-canary.b994146f6.0", - "@material/menu": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/select": "15.0.0-canary.b994146f6.0", - "@material/shape": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/tokens": "15.0.0-canary.b994146f6.0", - "@material/touch-target": "15.0.0-canary.b994146f6.0", - "@material/typography": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/data-table/-/data-table-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-j/7qplT9+sUpfe4pyWhPbl01qJA+OoNAG3VMJruBBR461ZBKyTi7ssKH9yksFGZ8eCEPkOsk/+kDxsiZvRWkeQ==", + "requires": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/checkbox": "15.0.0-canary.bc9ae6c9c.0", + "@material/density": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/icon-button": "15.0.0-canary.bc9ae6c9c.0", + "@material/linear-progress": "15.0.0-canary.bc9ae6c9c.0", + "@material/list": "15.0.0-canary.bc9ae6c9c.0", + "@material/menu": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/select": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/touch-target": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "@material/density": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/density/-/density-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-m8l0vuoWSoAPItBpWp5eZDvitUcB2JWoO8V486hLgdveVcKgXG09xWM43ScH+PLXAWjzr5olDEuJ2tvfkN3SpQ==", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/density/-/density-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-Zt3u07fXrBWLW06Tl5fgvjicxNQMkFdawLyNTzZ5TvbXfVkErILLePwwGaw8LNcvzqJP6ABLA8jiR+sKNoJQCg==", "requires": { "tslib": "^2.1.0" } }, "@material/dialog": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/dialog/-/dialog-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-JucU92yh8cfZQpyRBunHr6uohacePLYmhcPaGpkAGQ1b+zCznEsNs55tjhaVQNoj91XA9rrBqtL6Otg+fxFJtQ==", - "requires": { - "@material/animation": "15.0.0-canary.b994146f6.0", - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/button": "15.0.0-canary.b994146f6.0", - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/elevation": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/icon-button": "15.0.0-canary.b994146f6.0", - "@material/ripple": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/shape": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/tokens": "15.0.0-canary.b994146f6.0", - "@material/touch-target": "15.0.0-canary.b994146f6.0", - "@material/typography": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/dialog/-/dialog-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-o+9a/fmwJ9+gY3Z/uhj/PMVJDq7it1NTWKJn2GwAKdB+fDkT4hb9qEdcxMPyvJJ5ups+XiKZo03+tZrD+38c1w==", + "requires": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/button": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/icon-button": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/touch-target": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "@material/dom": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/dom/-/dom-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-DiUsTezrCi4iytjIn7xXoXZSNFvuTrVVZgc7cR9cW8yu2Hpz8bPf87PacVn4IP9OsNwy/dCDMk1Kcq/DMh7gXQ==", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/dom/-/dom-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-ly78R7aoCJtundSUu0UROU+5pQD5Piae0Y1MkN6bs0724azeazX1KeXFeaf06JOXnlr5/41ol+fSUPowjoqnOg==", "requires": { - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "@material/drawer": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/drawer/-/drawer-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-Kbuf32V0eX69amvCVbAjNSabNDerZWyG8ip466EfQHRh0OUZwvsbhLp9FZOB7AyR+/bQiHf3mVLcombOdmdkcQ==", - "requires": { - "@material/animation": "15.0.0-canary.b994146f6.0", - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/elevation": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/list": "15.0.0-canary.b994146f6.0", - "@material/ripple": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/shape": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/typography": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/drawer/-/drawer-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-PFL4cEFnt7VTxDsuspFVNhsFDYyumjU0VWfj3PWB7XudsEfQ3lo85D3HCEtTTbRsCainGN8bgYNDNafLBqiigw==", + "requires": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/list": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "@material/elevation": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/elevation/-/elevation-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-l2YDNgBajSI6oA2l6gaeYCTGHRao657syqQ/tv95/Hkcee9900A4RrsxCwSxOqqAs5pZZDEJ33kFJjj27nqZDw==", - "requires": { - "@material/animation": "15.0.0-canary.b994146f6.0", - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/elevation/-/elevation-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-Ro+Pk8jFuap+T0B0shA3xI1hs2b89dNQ2EIPCNjNMp87emHKAzJfhKb7EZGIwv3+gFLlVaLyIVkb94I89KLsyg==", + "requires": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "@material/fab": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/fab/-/fab-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-ExyDVkNWINpns41Ahj4u8I/OhiVkqI0nmcqjFRtgTJMmKEd4NhlvqIxE7gakAlyS68riJu5UleqTSTVmt8mv2Q==", - "requires": { - "@material/animation": "15.0.0-canary.b994146f6.0", - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/elevation": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/focus-ring": "15.0.0-canary.b994146f6.0", - "@material/ripple": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/shape": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/tokens": "15.0.0-canary.b994146f6.0", - "@material/touch-target": "15.0.0-canary.b994146f6.0", - "@material/typography": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/fab/-/fab-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-dvU0KWMRglwJEQwmQtFAmJcAjzg9VFF6Aqj78bJYu/DAIGFJ1VTTTSgoXM/XCm1YyQEZ7kZRvxBO37CH54rSDg==", + "requires": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/focus-ring": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/touch-target": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "@material/feature-targeting": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/feature-targeting/-/feature-targeting-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-HR/FjSQmza98B1DF80MRjODyfOI9r7wXkPSts/cLQsYkpwZ5uJmxhvQKjDCeYVpMV0lQuvuvVOQo7uD44TdWEg==", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/feature-targeting/-/feature-targeting-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-wkDjVcoVEYYaJvun28IXdln/foLgPD7n9ZC9TY76GErGCwTq+HWpU6wBAAk+ePmpRFDayw4vI4wBlaWGxLtysQ==", "requires": { "tslib": "^2.1.0" } }, "@material/floating-label": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/floating-label/-/floating-label-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-g64talBNWCS0FUfLWal0uB637gUciSIqYxFzSW//LglTtbZLGK2J4+9gAEswQGnKeO4ux08EN2n1ZcMDYQ58ow==", - "requires": { - "@material/animation": "15.0.0-canary.b994146f6.0", - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/typography": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/floating-label/-/floating-label-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-bUWPtXzZITOD/2mkvLkEPO1ngDWmb74y0Kgbz6llHLOQBtycyJIpuoQJ1q2Ez0NM/tFLwPphhAgRqmL3YQ/Kzw==", + "requires": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "@material/focus-ring": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/focus-ring/-/focus-ring-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-87qEMuXsCvlQfTiimnzJUZoebnIXWcMtRZevNLymN9Y0t9jGckQxZPmrI0llRkpyiR/Ewhec5SI/JGrFlYHnsA==", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/focus-ring/-/focus-ring-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-cZHThVose3GvAlJzpJoBI1iqL6d1/Jj9hXrR+r8Mwtb1hBIUEG3hxfsRd4vGREuzROPlf0OgNf/V+YHoSwgR5w==", "requires": { - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0" + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0" } }, "@material/form-field": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/form-field/-/form-field-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-Tg1SQQaopvXMyDEYxGTWnhCWQmNcWVIoKMLmle9P/gi2p8ulcj0iOCPYf+3ECqUBVozOmTPKlYOOiRwtKStAeA==", - "requires": { - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/ripple": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/typography": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/form-field/-/form-field-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-+JFXy5X44Gue1CbZZAQ6YejnI203lebYwL0i6k0ylDpWHEOdD5xkF2PyHR28r9/65Ebcbwbff6q7kI1SGoT7MA==", + "requires": { + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "@material/icon-button": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/icon-button/-/icon-button-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-X6DvOv4jpymHUjI7ZAbO946nDgGYKDwPZfkRzBE84gv2XEr2qfMuABhojxkYubRbt03oauBdcJVVMFCXkVhArQ==", - "requires": { - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/density": "15.0.0-canary.b994146f6.0", - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/elevation": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/focus-ring": "15.0.0-canary.b994146f6.0", - "@material/ripple": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/touch-target": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/icon-button/-/icon-button-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-1a0MHgyIwOs4RzxrVljsqSizGYFlM1zY2AZaLDsgT4G3kzsplTx8HZQ022GpUCjAygW+WLvg4z1qAhQHvsbqlw==", + "requires": { + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/density": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/focus-ring": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/touch-target": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "@material/image-list": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/image-list/-/image-list-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-kf903XFF1P+V5ZPXCt+7R6c55g4UyQE1ZHkTViCIJfd52gU40bHODMhTQy/ywBkwDeJfNk8uf1V1IM24WQYpxA==", - "requires": { - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/shape": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/typography": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/image-list/-/image-list-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-WKWmiYap2iu4QdqmeUSliLlN4O2Ueqa0OuVAYHn/TCzmQ2xmnhZ1pvDLbs6TplpOmlki7vFfe+aSt5SU9gwfOQ==", + "requires": { + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "@material/layout-grid": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/layout-grid/-/layout-grid-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-OALBSGue8g1/mEwLYYi2d950dJFpNYKW87jPS9/KM65JKMyxoU7tU2d4An1BuyqK0r9sopGq6Pn/zhill0iLaw==", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/layout-grid/-/layout-grid-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-5GqmT6oTZhUGWIb+CLD0ZNyDyTiJsr/rm9oRIi3+vCujACwxFkON9tzBlZohdtFS16nuzUusthN6Jt9UrJcN6Q==", "requires": { "tslib": "^2.1.0" } }, "@material/line-ripple": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/line-ripple/-/line-ripple-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-evjZxCu4iodiKtW8N0xjY8ACRXm3sY+4rAmq3vV5BmHWAJ3BobjbFYslDMZQ+4mu3HmwMatbJehKxHegahitNg==", - "requires": { - "@material/animation": "15.0.0-canary.b994146f6.0", - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/line-ripple/-/line-ripple-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-8S30WXEuUdgDdBulzUDlPXD6qMzwCX9SxYb5mGDYLwl199cpSGdXHtGgEcCjokvnpLhdZhcT1Dsxeo1g2Evh5Q==", + "requires": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "@material/linear-progress": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/linear-progress/-/linear-progress-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-jlXh+tIj+/o0Ks7fHdC/24fH6IXCAl2vF52U6NwT39ESrlwmlLhp3gtag5GSBHN5E7Z09nK871Yo1G/b1F+COg==", - "requires": { - "@material/animation": "15.0.0-canary.b994146f6.0", - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/progress-indicator": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/linear-progress/-/linear-progress-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-6EJpjrz6aoH2/gXLg9iMe0yF2C42hpQyZoHpmcgTLKeci85ktDvJIjwup8tnk8ULQyFiGiIrhXw2v2RSsiFjvQ==", + "requires": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/progress-indicator": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "@material/list": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/list/-/list-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-kY/i6VvFBb/W3VvCPvWRMzWvu7mvNFJ+R8ijfawDoAXiv4fj42GO4iFyTcFXaUevEPKp791pN/09BMJQ6jYEvA==", - "requires": { - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/density": "15.0.0-canary.b994146f6.0", - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/ripple": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/shape": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/tokens": "15.0.0-canary.b994146f6.0", - "@material/typography": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/list/-/list-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-TQ1ppqiCMQj/P7bGD4edbIIv4goczZUoiUAaPq/feb1dflvrFMzYqJ7tQRRCyBL8nRhJoI2x99tk8Q2RXvlGUQ==", + "requires": { + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/density": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "@material/menu": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/menu/-/menu-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-y6smNmLJ+U0DoXWbyqzW+VW/uWDuklhdGHc5MbZrTOhsKkhvoTVNMSOa+NFPU4gTwrplvUjaUvnIsQ0wygwD3g==", - "requires": { - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/elevation": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/list": "15.0.0-canary.b994146f6.0", - "@material/menu-surface": "15.0.0-canary.b994146f6.0", - "@material/ripple": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/shape": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/tokens": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/menu/-/menu-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-IlAh61xzrzxXs38QZlt74UYt8J431zGznSzDtB1Fqs6YFNd11QPKoiRXn1J2Qu/lUxbFV7i8NBKMCKtia0n6/Q==", + "requires": { + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/list": "15.0.0-canary.bc9ae6c9c.0", + "@material/menu-surface": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "@material/menu-surface": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/menu-surface/-/menu-surface-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-StmM3lrRn1iMEZfq532jpMNppqyBBy68FbPurKEsHuP/3q+CscfnwjrS9ym+JcHqXKMHnQXbL/49ymffRGX2AQ==", - "requires": { - "@material/animation": "15.0.0-canary.b994146f6.0", - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/elevation": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/shape": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/menu-surface/-/menu-surface-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-dMtSPN+olTWE+08M5qe4ea1IZOhVryYqzK0Gyb2u1G75rSArUxCOB5rr6OC/ST3Mq3RS6zGuYo7srZt4534K9Q==", + "requires": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "@material/notched-outline": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/notched-outline/-/notched-outline-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-UZxU8jXM2t/bk/CiO0K+TSPspuJRZIyrYlIS0gd+qq/u8Gi2DpALBlLAh9Jeu46IUg4YGlPsNWYfe8p3QAVyoA==", - "requires": { - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/floating-label": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/shape": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/notched-outline/-/notched-outline-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-WuurMg44xexkvLTBTnsO0A+qnzFjpcPdvgWBGstBepYozsvSF9zJGdb1x7Zv1MmqbpYh/Ohnuxtb/Y3jOh6irg==", + "requires": { + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/floating-label": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "@material/progress-indicator": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/progress-indicator/-/progress-indicator-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-VT+mOQhohaM+pBX1rknbVOI6JCGKg9NiOHBoYljIvnexNeILE+mW9g6mtQ0ZCJPz0oMmiSAMLcuxMIcBXx84Xw==", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/progress-indicator/-/progress-indicator-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-uOnsvqw5F2fkeTnTl4MrYzjI7KCLmmLyZaM0cgLNuLsWVlddQE+SGMl28tENx7DUK3HebWq0FxCP8f25LuDD+w==", "requires": { "tslib": "^2.1.0" } }, "@material/radio": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/radio/-/radio-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-U/RR2lVNWwEO2+kJtGz9XzvnOF0gAZn1krMY0z/eU9Wnl0OgPZbqQrxXMoVNv1pzKYSEwZQEGado/rv8qp7piA==", - "requires": { - "@material/animation": "15.0.0-canary.b994146f6.0", - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/density": "15.0.0-canary.b994146f6.0", - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/focus-ring": "15.0.0-canary.b994146f6.0", - "@material/ripple": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/touch-target": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/radio/-/radio-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-ehzOK+U1IxQN+OQjgD2lsnf1t7t7RAwQzeO6Czkiuid29ookYbQynWuLWk7NW8H8ohl7lnmfqTP1xSNkkL/F0g==", + "requires": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/density": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/focus-ring": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/touch-target": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "@material/ripple": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/ripple/-/ripple-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-WzIbc8wYTzMOczqGXVCBPdNcv/73Ef8FwcQYsscGMaqCzgVsdpoqilTfsx7Ryyz6dQbyfmJqp7s+YpPujcezOA==", - "requires": { - "@material/animation": "15.0.0-canary.b994146f6.0", - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/ripple/-/ripple-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-JfLW+g3GMVDv4cruQ19+HUxpKVdWCldFlIPw1UYezz2h3WTNDy05S3uP2zUdXzZ01C3dkBFviv4nqZ0GCT16MA==", + "requires": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "@material/rtl": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/rtl/-/rtl-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-H/W6BVn4Ygfkrf/FgSrNhbu1uY7PST2wlsjEYQt06EfAM0CDHEwSL1MwV4FmpQA/r40Q0PqoLN6moDrtCe5S8g==", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/rtl/-/rtl-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-SkKLNLFp5QtG7/JEFg9R92qq4MzTcZ5As6sWbH7rRg6ahTHoJEuqE+pOb9Vrtbj84k5gtX+vCYPvCILtSlr2uw==", "requires": { - "@material/theme": "15.0.0-canary.b994146f6.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "@material/segmented-button": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/segmented-button/-/segmented-button-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-jd+f4BTnU0tghxBpAM/XdVmruDXSoQ88TYSFWbrhulS+/c/ooCZURWvVC4mHNej+QR/fODkx4adbqkBiwwCtMw==", - "requires": { - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/elevation": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/ripple": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/touch-target": "15.0.0-canary.b994146f6.0", - "@material/typography": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/segmented-button/-/segmented-button-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-YDwkCWP9l5mIZJ7pZJZ2hMDxfBlIGVJ+deNzr8O+Z7/xC5LGXbl4R5aPtUVHygvXAXxpf5096ZD+dSXzYzvWlw==", + "requires": { + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/touch-target": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "@material/select": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/select/-/select-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-5thEQS+B17JSm3I8D+mqQe2G3ArVnXJALTEEE9FmMUKwKYkrsLplm3FYuEXERZGJnYeTRdkdmhYY/YeocfZoyA==", - "requires": { - "@material/animation": "15.0.0-canary.b994146f6.0", - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/density": "15.0.0-canary.b994146f6.0", - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/elevation": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/floating-label": "15.0.0-canary.b994146f6.0", - "@material/line-ripple": "15.0.0-canary.b994146f6.0", - "@material/list": "15.0.0-canary.b994146f6.0", - "@material/menu": "15.0.0-canary.b994146f6.0", - "@material/menu-surface": "15.0.0-canary.b994146f6.0", - "@material/notched-outline": "15.0.0-canary.b994146f6.0", - "@material/ripple": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/shape": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/tokens": "15.0.0-canary.b994146f6.0", - "@material/typography": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/select/-/select-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-unfOWVf7T0sixVG+3k3RTuATfzqvCF6QAzA6J9rlCh/Tq4HuIBNDdV4z19IVu4zwmgWYxY0iSvqWUvdJJYwakQ==", + "requires": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/density": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/floating-label": "15.0.0-canary.bc9ae6c9c.0", + "@material/line-ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/list": "15.0.0-canary.bc9ae6c9c.0", + "@material/menu": "15.0.0-canary.bc9ae6c9c.0", + "@material/menu-surface": "15.0.0-canary.bc9ae6c9c.0", + "@material/notched-outline": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "@material/shape": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/shape/-/shape-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-sINM3gr3aLgdvqZVfqfXV5EB77owLLJjy+2NqchJ8ZPqucCJ+F/BsCBfLA2Wu3O4Sc9IpAEn/o1hzYm/CWAFAw==", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/shape/-/shape-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-Dsvr771ZKC46ODzoixLdGwlLEQLfxfLrtnRojXABoZf5G3o9KtJU+J+5Ld5aa960OAsCzzANuaub4iR88b1guA==", "requires": { - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "@material/slider": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/slider/-/slider-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-dyT72+Kp//AEajJxDUVoMoizUjf2uggVMGXOaQ7FhpGHuf7LC3EyEjrrJ15efFzYgTjdJUU1YQkCwGmdt6CQsA==", - "requires": { - "@material/animation": "15.0.0-canary.b994146f6.0", - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/elevation": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/ripple": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/tokens": "15.0.0-canary.b994146f6.0", - "@material/typography": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/slider/-/slider-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-3AEu+7PwW4DSNLndue47dh2u7ga4hDJRYmuu7wnJCIWJBnLCkp6C92kNc4Rj5iQY2ftJio5aj1gqryluh5tlYg==", + "requires": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "@material/snackbar": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/snackbar/-/snackbar-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-fEhPASJossScNpcrNYrrH8uU+rUf6+kw7/ZMrpUzzz1lVXliL28jTNEmU1nFpcDI4M2GXH+Z64f7vl2hiMDG8g==", - "requires": { - "@material/animation": "15.0.0-canary.b994146f6.0", - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/button": "15.0.0-canary.b994146f6.0", - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/elevation": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/icon-button": "15.0.0-canary.b994146f6.0", - "@material/ripple": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/shape": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/tokens": "15.0.0-canary.b994146f6.0", - "@material/typography": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/snackbar/-/snackbar-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-TwwQSYxfGK6mc03/rdDamycND6o+1p61WNd7ElZv1F1CLxB4ihRjbCoH7Qo+oVDaP8CTpjeclka+24RLhQq0mA==", + "requires": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/button": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/icon-button": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "@material/switch": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/switch/-/switch-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-czCXTUa30ILIf1J3exiuSVIRcodGATHexd3eWDq4sfHo4iMh4rBMaIxcqkmnb2iwE/mMTNyVfoauijx2QiNKrA==", - "requires": { - "@material/animation": "15.0.0-canary.b994146f6.0", - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/density": "15.0.0-canary.b994146f6.0", - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/elevation": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/focus-ring": "15.0.0-canary.b994146f6.0", - "@material/ripple": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/shape": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/tokens": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/switch/-/switch-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-OjUjtT0kRz1ASAsOS+dNzwMwvsjmqy5edK57692qmrP6bL4GblFfBDoiNJ6t0AN4OaKcmL5Hy/xNrTdOZW7Qqw==", + "requires": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/density": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/focus-ring": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", "safevalues": "^0.3.4", "tslib": "^2.1.0" } }, "@material/tab": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/tab/-/tab-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-ygswooiNdBNNDnQdbPX0nzDQu7oQlHo8vWZ0/xL4IPVEXabY5zCzsEbGNZw2u/syo56c/NHPyMsUmXDGRSXOvQ==", - "requires": { - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/elevation": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/focus-ring": "15.0.0-canary.b994146f6.0", - "@material/ripple": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/tab-indicator": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/tokens": "15.0.0-canary.b994146f6.0", - "@material/typography": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/tab/-/tab-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-s/L9otAwn/pZwVQZBRQJmPqYeNbjoEbzbjMpDQf/VBG/6dJ+aP03ilIBEkqo8NVnCoChqcdtVCoDNRtbU+yp6w==", + "requires": { + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/focus-ring": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/tab-indicator": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "@material/tab-bar": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/tab-bar/-/tab-bar-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-F9NegACnFEWMu1pAAypV4Jd7qROeffkvEgVO28Xxk/CvzZxFz8kAjYJZ+rI6RUhPX3BhXzwsz/AlLwsJMT2tnA==", - "requires": { - "@material/animation": "15.0.0-canary.b994146f6.0", - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/density": "15.0.0-canary.b994146f6.0", - "@material/elevation": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/tab": "15.0.0-canary.b994146f6.0", - "@material/tab-indicator": "15.0.0-canary.b994146f6.0", - "@material/tab-scroller": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/tokens": "15.0.0-canary.b994146f6.0", - "@material/typography": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/tab-bar/-/tab-bar-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-Xmtq0wJGfu5k+zQeFeNsr4bUKv7L+feCmUp/gsapJ655LQKMXOUQZtSv9ZqWOfrCMy55hoF1CzGFV+oN3tyWWQ==", + "requires": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/density": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/tab": "15.0.0-canary.bc9ae6c9c.0", + "@material/tab-indicator": "15.0.0-canary.bc9ae6c9c.0", + "@material/tab-scroller": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "@material/tab-indicator": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/tab-indicator/-/tab-indicator-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-8IH/DmwlZhQlw/2Y3aKrEvjEhZB+qbKUiyaij3BkTAexvyFeDBh5cLNjRpYkUJSGeSPhS6yu4SYzMHPmQEwQmA==", - "requires": { - "@material/animation": "15.0.0-canary.b994146f6.0", - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/tab-indicator/-/tab-indicator-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-despCJYi1GrDDq7F2hvLQkObHnSLZPPDxnOzU16zJ6FNYvIdszgfzn2HgAZ6pl5hLOexQ8cla6cAqjTDuaJBhQ==", + "requires": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "@material/tab-scroller": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/tab-scroller/-/tab-scroller-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-1MeWkr62OICfTv8oqhIZe6jFo0dKeMlUfB+/WcgnpoeMBszCOSlx5tQ4pedxUkuR3I+Z7rsTfSN0LavgF8bATA==", - "requires": { - "@material/animation": "15.0.0-canary.b994146f6.0", - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/tab": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/tab-scroller/-/tab-scroller-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-QWHG/EWxirj4V9u2IHz+OSY9XCWrnNrPnNgEufxAJVUKV/A8ma1DYeFSQqxhX709R8wKGdycJksg0Flkl7Gq7w==", + "requires": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/tab": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "@material/textfield": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/textfield/-/textfield-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-Kxb3DoJ5o8u3Y1gRMHKmWrDl1TirVxuf/UFrxPFiCE3J1SqiE2VQpakiD1emZwp+LSKtbRsQ/iILYLB/h7Wuvw==", - "requires": { - "@material/animation": "15.0.0-canary.b994146f6.0", - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/density": "15.0.0-canary.b994146f6.0", - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/floating-label": "15.0.0-canary.b994146f6.0", - "@material/line-ripple": "15.0.0-canary.b994146f6.0", - "@material/notched-outline": "15.0.0-canary.b994146f6.0", - "@material/ripple": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/shape": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/tokens": "15.0.0-canary.b994146f6.0", - "@material/typography": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/textfield/-/textfield-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-R3qRex9kCaZIAK8DuxPnVC42R0OaW7AB7fsFknDKeTeVQvRcbnV8E+iWSdqTiGdsi6QQHifX8idUrXw+O45zPw==", + "requires": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/density": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/floating-label": "15.0.0-canary.bc9ae6c9c.0", + "@material/line-ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/notched-outline": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "@material/theme": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/theme/-/theme-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-5tsZ92dAeUcZ9g9CrIkqX/GYc0M5DIfsydtI1PAidaBzr1Uokuh4rTZVQZBv7gyglF0yDua59lkb0I6wI9vxXg==", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/theme/-/theme-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-CpUwXGE0dbhxQ45Hu9r9wbJtO/MAlv5ER4tBHA9tp/K+SU+lDgurBE2touFMg5INmdfVNtdumxb0nPPLaNQcUg==", "requires": { - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "@material/tokens": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/tokens/-/tokens-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-jFqU7PtvGkrP8b8i2soCrYQInTrnZ1/rIPDi+Xm3sa/qSghCNwFrdJEqwcwtv1fPlJIOtzkIuVRYRmAP9rXQIQ==", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/tokens/-/tokens-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-nbEuGj05txWz6ZMUanpM47SaAD7soyjKILR+XwDell9Zg3bGhsnexCNXPEz2fD+YgomS+jM5XmIcaJJHg/H93Q==", "requires": { - "@material/elevation": "15.0.0-canary.b994146f6.0" + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0" } }, "@material/tooltip": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/tooltip/-/tooltip-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-bVzydXGn3fauHJ8pkh32DsdyRJXleeFQ4t7jZ/rcRik+n4G1BvYiblfuu3Z/OCC0m3TJDyMdJhd+sLqRDqLUUg==", - "requires": { - "@material/animation": "15.0.0-canary.b994146f6.0", - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/button": "15.0.0-canary.b994146f6.0", - "@material/dom": "15.0.0-canary.b994146f6.0", - "@material/elevation": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/shape": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/tokens": "15.0.0-canary.b994146f6.0", - "@material/typography": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/tooltip/-/tooltip-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-UzuXp0b9NuWuYLYpPguxrjbJnCmT/Cco8CkjI/6JajxaeA3o2XEBbQfRMTq8PTafuBjCHTc0b0mQY7rtxUp1Gg==", + "requires": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/button": "15.0.0-canary.bc9ae6c9c.0", + "@material/dom": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/tokens": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", "safevalues": "^0.3.4", "tslib": "^2.1.0" } }, "@material/top-app-bar": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/top-app-bar/-/top-app-bar-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-VHq0wX3OJE1TKvjO8Qtlu+rv5EGoqAhNLBcEjpUUGoqHH/gpd356FEuIqJId4pUh5jaWf8T4ZU9xVbQGMtntzw==", - "requires": { - "@material/animation": "15.0.0-canary.b994146f6.0", - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/elevation": "15.0.0-canary.b994146f6.0", - "@material/ripple": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/shape": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", - "@material/typography": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/top-app-bar/-/top-app-bar-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-vJWjsvqtdSD5+yQ/9vgoBtBSCvPJ5uF/DVssv8Hdhgs1PYaAcODUi77kdi0+sy/TaWyOsTkQixqmwnFS16zesA==", + "requires": { + "@material/animation": "15.0.0-canary.bc9ae6c9c.0", + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/elevation": "15.0.0-canary.bc9ae6c9c.0", + "@material/ripple": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/shape": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", + "@material/typography": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "@material/touch-target": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/touch-target/-/touch-target-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-X26Y9OWvIqYOHo+sC2VMvOoeQWlUR3/yb7uPdfq92Y44zlQ4Vexgq7nEUblEiXQ8Fj+d0T9rIhRh1y9PP3Z2dw==", - "requires": { - "@material/base": "15.0.0-canary.b994146f6.0", - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/rtl": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/touch-target/-/touch-target-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-AqYh9fjt+tv4ZE0C6MeYHblS2H+XwLbDl2mtyrK0DOEnCVQk5/l5ImKDfhrUdFWHvS4a5nBM4AA+sa7KaroLoA==", + "requires": { + "@material/base": "15.0.0-canary.bc9ae6c9c.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/rtl": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, "@material/typography": { - "version": "15.0.0-canary.b994146f6.0", - "resolved": "https://registry.npmjs.org/@material/typography/-/typography-15.0.0-canary.b994146f6.0.tgz", - "integrity": "sha512-sWU5W30WWqdw5P6bsRx9AbvMNcz/QvQg56Syr06V6nfgSztpeuo7TfPk2J+N0ArRALo1mUrkAPk66iWYQ2p/QA==", + "version": "15.0.0-canary.bc9ae6c9c.0", + "resolved": "https://registry.npmjs.org/@material/typography/-/typography-15.0.0-canary.bc9ae6c9c.0.tgz", + "integrity": "sha512-CKsG1zyv34AKPNyZC8olER2OdPII64iR2SzQjpqh1UUvmIFiMPk23LvQ1OnC5aCB14pOXzmVgvJt31r9eNdZ6Q==", "requires": { - "@material/feature-targeting": "15.0.0-canary.b994146f6.0", - "@material/theme": "15.0.0-canary.b994146f6.0", + "@material/feature-targeting": "15.0.0-canary.bc9ae6c9c.0", + "@material/theme": "15.0.0-canary.bc9ae6c9c.0", "tslib": "^2.1.0" } }, @@ -16617,29 +17393,49 @@ } }, "@schematics/angular": { - "version": "16.0.6", - "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-16.0.6.tgz", - "integrity": "sha512-8naIlMeY9p5iOZqc3D0reoN80xm/fQINrG8mqIOgIY6bDeqfFvMKfaozA3PbPLbZhl5Jyk7VfZnXb6ISN0KnxQ==", + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/@schematics/angular/-/angular-16.2.0.tgz", + "integrity": "sha512-Ib0/ZCkjWt7a5p3209JVwEWwf41v03K3ylvlxLIEo1ZGijAZAlrBj4GrA5YQ+TmPm2hRyt+owss7x91/x+i0Gw==", "dev": true, "requires": { - "@angular-devkit/core": "16.0.6", - "@angular-devkit/schematics": "16.0.6", + "@angular-devkit/core": "16.2.0", + "@angular-devkit/schematics": "16.2.0", "jsonc-parser": "3.2.0" } }, + "@sigstore/bundle": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@sigstore/bundle/-/bundle-1.1.0.tgz", + "integrity": "sha512-PFutXEy0SmQxYI4texPw3dd2KewuNqv7OuK1ZFtY2fM754yhvG2KdgwIhRnoEE2uHdtdGNQ8s0lb94dW9sELog==", + "dev": true, + "requires": { + "@sigstore/protobuf-specs": "^0.2.0" + } + }, "@sigstore/protobuf-specs": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.1.0.tgz", - "integrity": "sha512-a31EnjuIDSX8IXBUib3cYLDRlPMU36AWX4xS8ysLaNu4ZzUesDiPt83pgrW2X1YLMe5L2HbDyaKK5BrL4cNKaQ==", + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@sigstore/protobuf-specs/-/protobuf-specs-0.2.1.tgz", + "integrity": "sha512-XTWVxnWJu+c1oCshMLwnKvz8ZQJJDVOlciMfgpJBQbThVjKTCG8dwyhgLngBD2KN0ap9F/gOV8rFDEx8uh7R2A==", "dev": true }, + "@sigstore/sign": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@sigstore/sign/-/sign-1.0.0.tgz", + "integrity": "sha512-INxFVNQteLtcfGmcoldzV6Je0sbbfh9I16DM4yJPw3j5+TFP8X6uIiA18mvpEa9yyeycAKgPmOA3X9hVdVTPUA==", + "dev": true, + "requires": { + "@sigstore/bundle": "^1.1.0", + "@sigstore/protobuf-specs": "^0.2.0", + "make-fetch-happen": "^11.0.1" + } + }, "@sigstore/tuf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-1.0.2.tgz", - "integrity": "sha512-vjwcYePJzM01Ha6oWWZ9gNcdIgnzyFxfqfWzph483DPJTH8Tb7f7bQRRll3CYVkyH56j0AgcPAcl6Vg95DPF+Q==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@sigstore/tuf/-/tuf-1.0.3.tgz", + "integrity": "sha512-2bRovzs0nJZFlCN3rXirE4gwxCn97JNjMmwpecqlbgV9WcxX7WRuIrgzx/X7Ib7MYRbyUTpBYE0s2x6AmZXnlg==", "dev": true, "requires": { - "@sigstore/protobuf-specs": "^0.1.0", + "@sigstore/protobuf-specs": "^0.2.0", "tuf-js": "^1.1.7" } }, @@ -16794,6 +17590,14 @@ "@types/send": "*" } }, + "@types/fs-extra": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.2.tgz", + "integrity": "sha512-SvSrYXfWSc7R4eqnOzbQF4TZmfpNSM9FrSWLU3EUnWBuyZqNBOrv1B1JA3byUDPUl9z4Ab3jeZG2eDdySlgNMg==", + "requires": { + "@types/node": "*" + } + }, "@types/http-errors": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.1.tgz", @@ -16830,8 +17634,7 @@ "@types/node": { "version": "20.4.2", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.4.2.tgz", - "integrity": "sha512-Dd0BYtWgnWJKwO1jkmTrzofjK2QXXcai0dmtzvIBhcA+RsG5h8R3xlyta0kGOZRNfL9GuRtb1knmPEhQrePCEw==", - "dev": true + "integrity": "sha512-Dd0BYtWgnWJKwO1jkmTrzofjK2QXXcai0dmtzvIBhcA+RsG5h8R3xlyta0kGOZRNfL9GuRtb1knmPEhQrePCEw==" }, "@types/qs": { "version": "6.9.7", @@ -16887,6 +17690,11 @@ "@types/node": "*" } }, + "@types/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-+OpjSaq85gvlZAYINyzKpLeiFkSC4EsC6IIiT6v6TLSU5k5U83fHGj9Lel8oKEXM0HqgrMVCjXPDPVICtxF7EQ==" + }, "@types/sockjs": { "version": "0.3.33", "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.33.tgz", @@ -17058,6 +17866,11 @@ "@xtuc/long": "4.2.2" } }, + "@xmldom/xmldom": { + "version": "0.8.10", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.10.tgz", + "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw==" + }, "@xtuc/ieee754": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", @@ -17144,13 +17957,11 @@ } }, "agentkeepalive": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.3.0.tgz", - "integrity": "sha512-7Epl1Blf4Sy37j4v9f9FjICCh4+KAQOyXgHEwlyBiAQLbhKdq/i2QQU3amQalS/wPhdPzDXPL5DMR5bkn+YeWg==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", + "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==", "dev": true, "requires": { - "debug": "^4.1.0", - "depd": "^2.0.0", "humanize-ms": "^1.2.1" } }, @@ -17218,8 +18029,7 @@ "ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" }, "ansi-styles": { "version": "3.2.1", @@ -17271,12 +18081,22 @@ "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", "dev": true }, + "astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==" + }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "dev": true }, + "at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==" + }, "autoprefixer": { "version": "10.4.14", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz", @@ -17357,14 +18177,12 @@ "balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "dev": true + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" }, "base64id": { "version": "2.0.0", @@ -17378,6 +18196,11 @@ "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", "dev": true }, + "big-integer": { + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==" + }, "big.js": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", @@ -17456,6 +18279,14 @@ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", "dev": true }, + "bplist-parser": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.3.2.tgz", + "integrity": "sha512-apC2+fspHGI3mMKj+dGevkGo/tCqVB8jMb6i+OX+E29p0Iposz07fABkRIfVUPNd5A5VbuOz1bZbnmkKLYF+wQ==", + "requires": { + "big-integer": "1.6.x" + } + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -17497,6 +18328,11 @@ "ieee754": "^1.1.13" } }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==" + }, "buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -17647,8 +18483,7 @@ "chownr": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "dev": true + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" }, "chrome-trace-event": { "version": "1.0.3", @@ -18196,7 +19031,6 @@ "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, "requires": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -18207,7 +19041,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "requires": { "isexe": "^2.0.0" } @@ -18286,7 +19119,6 @@ "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "dev": true, "requires": { "ms": "2.1.2" } @@ -18318,8 +19150,7 @@ "define-lazy-prop": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", - "dev": true + "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==" }, "delayed-stream": { "version": "1.0.0", @@ -18463,11 +19294,25 @@ "integrity": "sha512-ux2LqN9JKRBDKXMT+78jtiBLPiXf+rLtYlsrOg5Qn7uv6Cbg7+9JyIalE3wcqkOdB2wPCUYNWAuL7suKRMHe9w==", "dev": true }, + "elementtree": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/elementtree/-/elementtree-0.1.7.tgz", + "integrity": "sha512-wkgGT6kugeQk/P6VZ/f4T+4HB41BVgNBq5CDIZVbQ02nvTVqAiVTbskxxu3eA/X96lMlfYOwnLQpN2v5E1zDEg==", + "requires": { + "sax": "1.1.4" + }, + "dependencies": { + "sax": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.1.4.tgz", + "integrity": "sha512-5f3k2PbGGp+YtKJjOItpg3P99IMD84E4HOvcfleTb5joCHNXYLsR9yWFPOYGgaeMPDubQILTCMdsFb2OMeOjtg==" + } + } + }, "emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, "emojis-list": { "version": "3.0.0", @@ -18552,8 +19397,7 @@ "env-paths": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", - "dev": true + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==" }, "err-code": { "version": "2.0.3", @@ -18925,6 +19769,14 @@ "websocket-driver": ">=0.5.1" } }, + "fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "requires": { + "pend": "~1.2.0" + } + }, "figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -19093,8 +19945,7 @@ "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, "fsevents": { "version": "2.3.2", @@ -19227,8 +20078,7 @@ "graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" }, "handle-thing": { "version": "2.0.1", @@ -19583,13 +20433,12 @@ "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, "ini": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/ini/-/ini-4.0.0.tgz", - "integrity": "sha512-t0ikzf5qkSFqRl1e6ejKBe+Tk2bsQd8ivEkcisyGXsku2t8NvXZ1Y3RRz5vxrDgOrTBOi13CvGsVoI5wVpd7xg==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz", + "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==", "dev": true }, "injection-js": { @@ -19723,8 +20572,7 @@ "is-docker": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", - "dev": true + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==" }, "is-extglob": { "version": "2.1.1", @@ -19735,8 +20583,7 @@ "is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" }, "is-glob": { "version": "4.0.3", @@ -19808,7 +20655,6 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, "requires": { "is-docker": "^2.0.0" } @@ -19828,8 +20674,7 @@ "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, "isobject": { "version": "3.0.1", @@ -20173,6 +21018,11 @@ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true }, + "kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==" + }, "klona": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/klona/-/klona-2.0.6.tgz", @@ -20547,8 +21397,7 @@ "minipass": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "dev": true + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==" }, "minipass-collect": { "version": "1.0.2", @@ -20577,15 +21426,23 @@ } }, "minipass-fetch": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.3.tgz", - "integrity": "sha512-n5ITsTkDqYkYJZjcRWzZt9qnZKCT7nKCosJhHoj7S7zD+BP4jVbWs+odsniw5TA3E0sLomhTKOKjF86wf11PuQ==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.4.tgz", + "integrity": "sha512-jHAqnA728uUpIaFm7NWsCnqKT6UqZz7GcI/bDpPATuwYyKwJwW0remxSCxUlKiEty+eopHGa3oc8WxgQ1FFJqg==", "dev": true, "requires": { "encoding": "^0.1.13", - "minipass": "^5.0.0", + "minipass": "^7.0.3", "minipass-sized": "^1.0.3", "minizlib": "^2.1.2" + }, + "dependencies": { + "minipass": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.3.tgz", + "integrity": "sha512-LhbbwCfz3vsb12j/WkWQPZfKTsgqIe1Nf/ti1pKjYESGLHIVjWU96G9/ljLH4F9mWNVhlQOm0VySdAWzf05dpg==", + "dev": true + } } }, "minipass-flush": { @@ -20697,7 +21554,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "dev": true, "requires": { "minipass": "^3.0.0", "yallist": "^4.0.0" @@ -20707,7 +21563,6 @@ "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, "requires": { "yallist": "^4.0.0" } @@ -20715,8 +21570,7 @@ "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } } }, @@ -20729,6 +21583,12 @@ "minimist": "^1.2.6" } }, + "moment": { + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", + "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "peer": true + }, "mrmime": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz", @@ -20738,8 +21598,7 @@ "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "multicast-dns": { "version": "7.2.5", @@ -20763,6 +21622,31 @@ "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==", "dev": true }, + "native-run": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/native-run/-/native-run-1.7.2.tgz", + "integrity": "sha512-2aahC8iXIO8BcvEukVMrYwL5sXurkuIGyQgfSGBto832W6ejV+cB5Ww+2/CRxmyozhbxARJ2OMpEGPV8sTqsrQ==", + "requires": { + "@ionic/utils-fs": "^3.1.6", + "@ionic/utils-terminal": "^2.3.3", + "bplist-parser": "^0.3.2", + "debug": "^4.3.4", + "elementtree": "^0.1.7", + "ini": "^3.0.1", + "plist": "^3.0.6", + "split2": "^4.1.0", + "through2": "^4.0.2", + "tslib": "^2.4.0", + "yauzl": "^2.10.0" + }, + "dependencies": { + "ini": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ini/-/ini-3.0.1.tgz", + "integrity": "sha512-it4HyVAUTKBc6m8e1iXWvXSTdndF7HbdN713+kvLrymxTaU4AUBWrJ4vEooP+V7fexnVD3LKcBshjGGPefSMUQ==" + } + } + }, "needle": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/needle/-/needle-3.2.0.tgz", @@ -20975,9 +21859,9 @@ } }, "npm-install-checks": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-6.1.1.tgz", - "integrity": "sha512-dH3GmQL4vsPtld59cOn8uY0iOqRmqKvV+DLGwNXV/Q7MDgD2QfOADWd/mFXcIE5LVhYYGjA3baz6W9JneqnuCw==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/npm-install-checks/-/npm-install-checks-6.2.0.tgz", + "integrity": "sha512-744wat5wAAHsxa4590mWO0tJ8PKxR8ORZsH9wGpQc3nWTzozMAgBN/XyqYw7mg3yqLM8dLwEnwSfKMmXAjF69g==", "dev": true, "requires": { "semver": "^7.1.1" @@ -21122,7 +22006,6 @@ "version": "8.4.2", "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", - "dev": true, "requires": { "define-lazy-prop": "^2.0.0", "is-docker": "^2.1.1", @@ -21255,9 +22138,9 @@ "dev": true }, "pacote": { - "version": "15.1.3", - "resolved": "https://registry.npmjs.org/pacote/-/pacote-15.1.3.tgz", - "integrity": "sha512-aRts8cZqxiJVDitmAh+3z+FxuO3tLNWEmwDRPEpDDiZJaRz06clP4XX112ynMT5uF0QNoMPajBBHnaStUEPJXA==", + "version": "15.2.0", + "resolved": "https://registry.npmjs.org/pacote/-/pacote-15.2.0.tgz", + "integrity": "sha512-rJVZeIwHTUta23sIZgEIM62WYwbmGbThdbnkt81ravBplQv+HjyroqnLRNH2+sLJHcGZmLRmhPwACqhfTcOmnA==", "dev": true, "requires": { "@npmcli/git": "^4.0.0", @@ -21371,8 +22254,7 @@ "path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==" }, "path-parse": { "version": "1.0.7", @@ -21384,7 +22266,6 @@ "version": "1.10.1", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", - "dev": true, "requires": { "lru-cache": "^9.1.1 || ^10.0.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" @@ -21393,8 +22274,7 @@ "lru-cache": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.0.tgz", - "integrity": "sha512-svTf/fzsKHffP42sujkO/Rjs37BCIsQVRCeNYIm9WN8rgT7ffoUnRtZCqU+6BqcSBdv8gwJeTz8knJpgACeQMw==", - "dev": true + "integrity": "sha512-svTf/fzsKHffP42sujkO/Rjs37BCIsQVRCeNYIm9WN8rgT7ffoUnRtZCqU+6BqcSBdv8gwJeTz8knJpgACeQMw==" } } }, @@ -21410,6 +22290,11 @@ "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==" + }, "picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", @@ -21450,6 +22335,16 @@ "find-up": "^4.0.0" } }, + "plist": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", + "integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==", + "requires": { + "@xmldom/xmldom": "^0.8.8", + "base64-js": "^1.5.1", + "xmlbuilder": "^15.1.1" + } + }, "postcss": { "version": "8.4.24", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.24.tgz", @@ -21588,6 +22483,22 @@ "retry": "^0.12.0" } }, + "prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "requires": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "dependencies": { + "kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==" + } + } + }, "proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", @@ -21726,7 +22637,6 @@ "version": "3.6.2", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "dev": true, "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -21954,8 +22864,7 @@ "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" }, "safer-buffer": { "version": "2.1.2", @@ -21992,9 +22901,7 @@ "sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true, - "optional": true + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" }, "schema-utils": { "version": "4.2.0", @@ -22024,10 +22931,9 @@ } }, "semver": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.4.0.tgz", - "integrity": "sha512-RgOxM8Mw+7Zus0+zcLEUn8+JfoLpj/huFTItQy2hsM4khuC1HYRDp0cU482Ewn/Fcy6bCjufD8vAj7voC66KQw==", - "dev": true, + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "requires": { "lru-cache": "^6.0.0" }, @@ -22036,7 +22942,6 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, "requires": { "yallist": "^4.0.0" } @@ -22044,8 +22949,7 @@ "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } } }, @@ -22215,7 +23119,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "requires": { "shebang-regex": "^3.0.0" } @@ -22223,8 +23126,7 @@ "shebang-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==" }, "shell-quote": { "version": "1.8.1", @@ -22246,26 +23148,65 @@ "signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, "sigstore": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/sigstore/-/sigstore-1.7.0.tgz", - "integrity": "sha512-KP7QULhWdlu3hlp+jw2EvgWKlOGOY9McLj/jrchLjHNlNPK0KWIwF919cbmOp6QiKXLmPijR2qH/5KYWlbtG9Q==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/sigstore/-/sigstore-1.9.0.tgz", + "integrity": "sha512-0Zjz0oe37d08VeOtBIuB6cRriqXse2e8w+7yIy2XSXjshRKxbc2KkhXjL229jXSxEm7UbcjS76wcJDGQddVI9A==", "dev": true, "requires": { - "@sigstore/protobuf-specs": "^0.1.0", - "@sigstore/tuf": "^1.0.1", + "@sigstore/bundle": "^1.1.0", + "@sigstore/protobuf-specs": "^0.2.0", + "@sigstore/sign": "^1.0.0", + "@sigstore/tuf": "^1.0.3", "make-fetch-happen": "^11.0.1" } }, + "sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==" + }, "slash": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", "dev": true }, + "slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "requires": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + } + } + }, "smart-buffer": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", @@ -22455,6 +23396,11 @@ "wbuf": "^1.7.3" } }, + "split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==" + }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -22491,7 +23437,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dev": true, "requires": { "safe-buffer": "~5.2.0" } @@ -22500,7 +23445,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -22522,7 +23466,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "requires": { "ansi-regex": "^5.0.1" } @@ -22573,7 +23516,6 @@ "version": "6.1.15", "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.15.tgz", "integrity": "sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A==", - "dev": true, "requires": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", @@ -22587,7 +23529,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "dev": true, "requires": { "minipass": "^3.0.0" }, @@ -22596,7 +23537,6 @@ "version": "3.3.6", "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, "requires": { "yallist": "^4.0.0" } @@ -22606,14 +23546,12 @@ "mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" }, "yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" } } }, @@ -22703,6 +23641,14 @@ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "dev": true }, + "through2": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "requires": { + "readable-stream": "3" + } + }, "thunky": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", @@ -22748,8 +23694,7 @@ "tree-kill": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz", - "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", - "dev": true + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==" }, "tslib": { "version": "2.6.0", @@ -22868,6 +23813,11 @@ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "dev": true }, + "untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==" + }, "update-browserslist-db": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.11.tgz", @@ -22890,8 +23840,7 @@ "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, "utils-merge": { "version": "1.0.1", @@ -23211,7 +24160,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, "requires": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -23222,7 +24170,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "requires": { "color-convert": "^2.0.1" } @@ -23231,7 +24178,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, "requires": { "color-name": "~1.1.4" } @@ -23239,8 +24185,7 @@ "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" } } }, @@ -23294,6 +24239,27 @@ "dev": true, "requires": {} }, + "xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "dependencies": { + "xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==" + } + } + }, + "xmlbuilder": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", + "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==" + }, "xxhashjs": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/xxhashjs/-/xxhashjs-0.2.2.tgz", @@ -23336,6 +24302,15 @@ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true }, + "yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "requires": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, "zone.js": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/zone.js/-/zone.js-0.13.1.tgz", diff --git a/angular-ui/package.json b/angular-ui/package.json index 3e4449a..de6cd3e 100644 --- a/angular-ui/package.json +++ b/angular-ui/package.json @@ -23,16 +23,19 @@ "@angular/core": "^16.0.0", "@angular/forms": "^16.0.0", "@angular/material": "^16.1.5", + "@angular/material-moment-adapter": "^16.2.6", "@angular/platform-browser": "^16.0.0", "@angular/platform-browser-dynamic": "^16.0.0", "@angular/router": "^16.0.0", + "@capacitor/cli": "^5.3.0", + "@capacitor/core": "^5.3.0", "rxjs": "~7.8.0", "tslib": "^2.3.0", "zone.js": "~0.13.0" }, "devDependencies": { "@angular-devkit/build-angular": "^16.0.6", - "@angular/cli": "~16.0.0", + "@angular/cli": "^16.2.0", "@angular/compiler-cli": "^16.0.0", "@openapitools/openapi-generator-cli": "^2.6.0", "@types/jasmine": "~4.3.0", diff --git a/angular-ui/projects/c4-soft/bff-api/.openapi-generator/FILES b/angular-ui/projects/c4-soft/bff-api/.openapi-generator/FILES index 271fd7d..8709672 100644 --- a/angular-ui/projects/c4-soft/bff-api/.openapi-generator/FILES +++ b/angular-ui/projects/c4-soft/bff-api/.openapi-generator/FILES @@ -3,13 +3,10 @@ api.module.ts api/api.ts api/bFF.service.ts api/bFF.serviceInterface.ts -api/backChannelLogoutController.service.ts -api/backChannelLogoutController.serviceInterface.ts configuration.ts encoder.ts index.ts model/loginOptionDto.ts model/models.ts -model/userInfoDto.ts param.ts variables.ts diff --git a/angular-ui/projects/c4-soft/quiz-api/.openapi-generator/FILES b/angular-ui/projects/c4-soft/quiz-api/.openapi-generator/FILES index a0dc9c1..bb16dfe 100644 --- a/angular-ui/projects/c4-soft/quiz-api/.openapi-generator/FILES +++ b/angular-ui/projects/c4-soft/quiz-api/.openapi-generator/FILES @@ -5,21 +5,23 @@ api/quizzes.service.ts api/quizzes.serviceInterface.ts api/skillTest.service.ts api/skillTest.serviceInterface.ts +api/users.service.ts +api/users.serviceInterface.ts configuration.ts encoder.ts index.ts -model/choice.ts model/choiceDto.ts model/choiceUpdateDto.ts model/models.ts -model/question.ts model/questionDto.ts model/questionUpdateDto.ts -model/quiz.ts model/quizDto.ts +model/quizRejectionDto.ts model/quizUpdateDto.ts model/skillTestDto.ts model/skillTestQuestionDto.ts -model/skillTestResultDto.ts +model/skillTestResultDetailsDto.ts +model/skillTestResultPreviewDto.ts +model/userInfoDto.ts param.ts variables.ts diff --git a/angular-ui/projects/quiz/src/app/app-routing.module.ts b/angular-ui/projects/quiz/src/app/app-routing.module.ts index 0297262..a0becf5 100644 --- a/angular-ui/projects/quiz/src/app/app-routing.module.ts +++ b/angular-ui/projects/quiz/src/app/app-routing.module.ts @@ -1,7 +1,17 @@ import { NgModule } from '@angular/core'; import { RouterModule, Routes } from '@angular/router'; +import { QuizSelectionPage } from './quiz-selection.page'; +import { QuizDetailsPage } from './quiz-details.page'; +import { SkillTestSelectionPage } from './skill-test-selection.page'; +import { SkillTestDetailsPage } from './skill-test-details.page'; -const routes: Routes = []; +export const routes: Routes = [ + { path: 'quizzes', component: QuizSelectionPage }, + { path: 'quizzes/:quizId', component: QuizDetailsPage }, + { path: 'tests', component: SkillTestSelectionPage }, + { path: 'tests/:quizId/:traineeName', component: SkillTestDetailsPage }, + { path: '**', redirectTo: '/quizzes' } +]; @NgModule({ imports: [RouterModule.forRoot(routes)], diff --git a/angular-ui/projects/quiz/src/app/app.component.ts b/angular-ui/projects/quiz/src/app/app.component.ts index 94031a2..af272ef 100644 --- a/angular-ui/projects/quiz/src/app/app.component.ts +++ b/angular-ui/projects/quiz/src/app/app.component.ts @@ -2,7 +2,7 @@ import { Component } from '@angular/core'; @Component({ selector: 'app-root', - template: ``, + template: ``, styles: [], }) export class AppComponent { diff --git a/angular-ui/projects/quiz/src/app/app.module.ts b/angular-ui/projects/quiz/src/app/app.module.ts index 87d94b0..4d1c2fc 100644 --- a/angular-ui/projects/quiz/src/app/app.module.ts +++ b/angular-ui/projects/quiz/src/app/app.module.ts @@ -1,14 +1,28 @@ import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; +import { DragDropModule } from '@angular/cdk/drag-drop'; import { MatButtonModule } from '@angular/material/button'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatDatepickerModule } from '@angular/material/datepicker'; +import { MatDialogModule } from '@angular/material/dialog'; +import { MatExpansionModule } from '@angular/material/expansion'; +import { MatFormFieldModule } from '@angular/material/form-field'; import { MatIconModule } from '@angular/material/icon'; +import { MatInputModule } from '@angular/material/input'; +import { MatListModule } from '@angular/material/list'; import { MatMenuModule } from '@angular/material/menu'; +import { MatProgressBarModule } from '@angular/material/progress-bar'; import { MatToolbarModule } from '@angular/material/toolbar'; +import { MatTooltipModule } from '@angular/material/tooltip'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { AppRoutingModule } from './app-routing.module'; +import { AppRoutingModule, routes } from './app-routing.module'; import { AppComponent } from './app.component'; +import { + MAT_MOMENT_DATE_FORMATS, + MomentDateAdapter, +} from '@angular/material-moment-adapter'; import { Configuration as BffApiConfiguration, ConfigurationParameters as BffApiConfigurationParameters, @@ -16,12 +30,33 @@ import { } from '@c4-soft/bff-api'; import { HttpClientModule } from '@angular/common/http'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MatMomentDateModule } from '@angular/material-moment-adapter'; +import { + DateAdapter, + MAT_DATE_FORMATS, + MAT_DATE_LOCALE, +} from '@angular/material/core'; +import { MatSelectModule } from '@angular/material/select'; +import { provideRouter, withComponentInputBinding } from '@angular/router'; import { Configuration as QuizApiConfiguration, ConfigurationParameters as QuizApiConfigurationParameters, ApiModule as QuizApiModule, } from '@c4-soft/quiz-api'; +import { ChoiceItemComponent } from './choice-item.component'; +import { ConfirmationDialog } from './confirmation.dialog'; +import { ErrorDialog } from './error.dialog'; +import { QuestionCreationDialog } from './question-creation.dialog'; +import { QuestionExpansionPannelComponent } from './question-expansion-pannel.component'; +import { QuizCreationDialog } from './quiz-creation.dialog'; +import { QuizDetailsPage } from './quiz-details.page'; +import { QuizRejectionDialog } from './quiz-rejection.dialog'; +import { QuizSelectionPage } from './quiz-selection.page'; +import { SkillTestDetailsPage } from './skill-test-details.page'; +import { SkillTestSelectionPage } from './skill-test-selection.page'; import { ToolbarComponent } from './toolbar.component'; +import { SkillTestResultDialog } from './skill-test-result.dialog'; export function bffApiConfigFactory(): BffApiConfiguration { const params: BffApiConfigurationParameters = { @@ -32,26 +67,63 @@ export function bffApiConfigFactory(): BffApiConfiguration { export function quizApiConfigFactory(): QuizApiConfiguration { const params: QuizApiConfigurationParameters = { - basePath: '/bff/quizzes/v1/', + basePath: '/bff/v1', }; return new QuizApiConfiguration(params); } @NgModule({ - declarations: [AppComponent, ToolbarComponent], + declarations: [ + AppComponent, + ToolbarComponent, + QuizSelectionPage, + QuizCreationDialog, + ErrorDialog, + QuizDetailsPage, + QuestionCreationDialog, + ChoiceItemComponent, + QuestionExpansionPannelComponent, + ConfirmationDialog, + QuizRejectionDialog, + SkillTestSelectionPage, + SkillTestDetailsPage, + SkillTestResultDialog, + ], imports: [ BrowserModule, + FormsModule, + ReactiveFormsModule, HttpClientModule, AppRoutingModule, BrowserAnimationsModule, BffApiModule.forRoot(bffApiConfigFactory), QuizApiModule.forRoot(quizApiConfigFactory), + DragDropModule, MatButtonModule, + MatCheckboxModule, + MatDatepickerModule, + MatDialogModule, + MatExpansionModule, + MatFormFieldModule, MatIconModule, + MatInputModule, + MatListModule, MatMenuModule, + MatMomentDateModule, + MatProgressBarModule, + MatSelectModule, MatToolbarModule, + MatTooltipModule, + ], + providers: [ + provideRouter(routes, withComponentInputBinding()), + { + provide: DateAdapter, + useClass: MomentDateAdapter, + deps: [MAT_DATE_LOCALE], + }, + { provide: MAT_DATE_FORMATS, useValue: MAT_MOMENT_DATE_FORMATS }, ], - providers: [], bootstrap: [AppComponent], }) export class AppModule {} diff --git a/angular-ui/projects/quiz/src/app/choice-item.component.ts b/angular-ui/projects/quiz/src/app/choice-item.component.ts new file mode 100644 index 0000000..92772f7 --- /dev/null +++ b/angular-ui/projects/quiz/src/app/choice-item.component.ts @@ -0,0 +1,188 @@ +import { + Component, + EventEmitter, + Input, + OnDestroy, + OnInit, + Output, +} from '@angular/core'; +import { FormControl, FormGroup, Validators } from '@angular/forms'; +import { MatDialog } from '@angular/material/dialog'; +import { ChoiceDto, ChoiceUpdateDto, QuizzesApi } from '@c4-soft/quiz-api'; +import { Observable, Subscription, debounceTime } from 'rxjs'; +import { ConfirmationDialog } from './confirmation.dialog'; +import { ErrorDialog } from './error.dialog'; + +export interface ChoiceEvent { + choiceId: number; + isSelected: boolean; +} + +@Component({ + selector: 'app-choice-item', + template: `
+ + Label + + + + done + close + +
`, + styles: [], +}) +export class ChoiceItemComponent implements OnInit, OnDestroy { + @Input() + choice!: ChoiceDto; + + @Input() + isInEditMode$!: Observable; + + @Input() + isValidated$!: Observable; + + @Input() + questionAnswer!: Map; + + @Input() + isPerQuestionResult!: boolean + + @Output() + readonly onDeleted = new EventEmitter(); + + private sub!: Subscription; + + form!: FormGroup; + + constructor(private quizApi: QuizzesApi, private dialog: MatDialog) {} + + ngOnInit(): void { + this.form = new FormGroup({ + label: new FormControl(this.choice?.label, [Validators.required]), + isGood: new FormControl(false), + }); + this.isInEditMode$.subscribe((isInEditMode) => { + if (isInEditMode) { + this.form.controls['isGood'].patchValue(this.choice?.isGood); + } + this.sub?.unsubscribe(); + if (isInEditMode) { + this.sub = this.form.valueChanges + .pipe(debounceTime(750)) + .subscribe((formValue) => { + if (this.form.valid) { + const dto: ChoiceUpdateDto = { + label: formValue.label || '', + isGood: formValue.isGood || false, + }; + if (this.choice.choiceId > -1) { + this.quizApi + .updateChoice( + this.choice.quizId, + this.choice.questionId, + this.choice.choiceId, + dto + ) + .subscribe({ + error: (error) => { + this.dialog.open(ErrorDialog, { data: { error } }); + }, + }); + } else { + this.quizApi + .addChoice( + this.choice.quizId, + this.choice.questionId, + dto, + 'response' + ) + .subscribe({ + next: (resp) => { + this.choice.choiceId = parseInt( + resp.headers.get('Location') || '-1' + ); + }, + error: (error) => { + this.dialog.open(ErrorDialog, { data: { error } }); + }, + }); + } + } + }); + } else { + this.sub = this.form.valueChanges.subscribe((formValue) => { + if (formValue.hasOwnProperty('isGood')) { + this.questionAnswer.set( + this.choice.choiceId, + formValue.isGood || false + ); + } + }); + } + }); + + this.isValidated$.subscribe((disabled) => { + disabled + ? this.form.controls['isGood']?.disable() + : this.form.controls['isGood']?.enable(); + }); + } + + get isCorrect(): boolean { + return ( + (this.choice.isGood && this.questionAnswer.get(this.choice.choiceId)) || + (!this.choice.isGood && !this.questionAnswer.get(this.choice.choiceId)) + ); + } + + ngOnDestroy() { + this.sub?.unsubscribe(); + } + + delete() { + this.dialog + .open(ConfirmationDialog, { + data: { message: 'Delete choice permanently?' }, + }) + .afterClosed() + .subscribe((isConfirmed) => { + if (!isConfirmed) { + return; + } + if (this.choice.choiceId < 0) { + this.onDeleted.emit(); + } else { + this.quizApi + .deleteChoice( + this.choice.quizId, + this.choice.questionId, + this.choice.choiceId + ) + .subscribe({ + complete: () => { + this.onDeleted.emit(); + }, + error: (error) => { + this.dialog.open(ErrorDialog, { data: { error } }); + }, + }); + } + }); + } +} diff --git a/angular-ui/projects/quiz/src/app/confirmation.dialog.ts b/angular-ui/projects/quiz/src/app/confirmation.dialog.ts new file mode 100644 index 0000000..66468c7 --- /dev/null +++ b/angular-ui/projects/quiz/src/app/confirmation.dialog.ts @@ -0,0 +1,33 @@ +import { Component, Inject } from '@angular/core'; +import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; + +export interface ConfirmationDialogData { + message: string; +} + +@Component({ + selector: 'app-confirmation', + template: ` + Confirmation + + {{ data.message }} + + + + `, + styles: [], +}) +export class ConfirmationDialog { + constructor( + private dialog: MatDialogRef, + @Inject(MAT_DIALOG_DATA) readonly data: ConfirmationDialogData + ) {} + + cancel() { + this.dialog.close(false); + } + + confirm() { + this.dialog.close(true); + } +} diff --git a/angular-ui/projects/quiz/src/app/error.dialog.ts b/angular-ui/projects/quiz/src/app/error.dialog.ts new file mode 100644 index 0000000..5caf9bc --- /dev/null +++ b/angular-ui/projects/quiz/src/app/error.dialog.ts @@ -0,0 +1,38 @@ +import { Component, Inject } from '@angular/core'; +import { MAT_DIALOG_DATA } from '@angular/material/dialog'; + +export interface ErrorDialogData { + error: any; +} + +@Component({ + selector: 'app-error', + template: ` + {{ title }} + +
+ {{ message }} +
`, + styles: [], +}) +export class ErrorDialog { + constructor(@Inject(MAT_DIALOG_DATA) public data: ErrorDialogData) {} + + get title() { + var title = 'Error'; + if (this.data.error.hasOwnProperty('status')) { + title = `${title}: ${this.data.error['status']}`; + if (this.data.error.hasOwnProperty('statusText')) { + title = `${title} ${this.data.error['statusText']}`; + } + } + return title; + } + + get message() { + if (this.data.error.hasOwnProperty('message')) { + return this.data.error['message']; + } + return this.data.error; + } +} diff --git a/angular-ui/projects/quiz/src/app/question-creation.dialog.ts b/angular-ui/projects/quiz/src/app/question-creation.dialog.ts new file mode 100644 index 0000000..c91fa37 --- /dev/null +++ b/angular-ui/projects/quiz/src/app/question-creation.dialog.ts @@ -0,0 +1,86 @@ +import { Component, Inject } from '@angular/core'; +import { FormControl, FormGroup, Validators } from '@angular/forms'; +import { MAT_DIALOG_DATA, MatDialog, MatDialogRef } from '@angular/material/dialog'; +import { Router } from '@angular/router'; +import { QuizzesApi } from '@c4-soft/quiz-api'; +import { ErrorDialog } from './error.dialog'; + +export interface QuestionCreationDialogData { + quizId: number +} + +@Component({ + selector: 'app-question-creation', + template: ` + New Question + +
+ + Label + + + + Comment + + + +
`, + styles: [], +}) + +export class QuestionCreationDialog { + creationForm = new FormGroup({ + labelInput: new FormControl('', [Validators.required]), + commentInput: new FormControl('', []), + }); + + constructor( + @Inject(MAT_DIALOG_DATA) private data: QuestionCreationDialogData, + private dialogRef: MatDialogRef, + private quizApi: QuizzesApi, + private dialog: MatDialog, + ) {} + + createQuestion() { + if(this.creationForm.invalid) { + return + } + this.quizApi + .addQuestion( + this.data.quizId, + { + label: this.creationForm.controls.labelInput.value || '', + comment: this.creationForm.controls.commentInput.value || '', + }, + 'response' + ) + .subscribe({ + next: (resp) => { + var questionId = resp.headers.get('Location') + this.dialogRef.close(questionId ? +questionId : null); + }, + error: (error) => { + this.dialogRef.close(null); + this.dialog.open(ErrorDialog, { data: { error } }); + }, + }); + } +} diff --git a/angular-ui/projects/quiz/src/app/question-expansion-pannel.component.ts b/angular-ui/projects/quiz/src/app/question-expansion-pannel.component.ts new file mode 100644 index 0000000..af2474f --- /dev/null +++ b/angular-ui/projects/quiz/src/app/question-expansion-pannel.component.ts @@ -0,0 +1,280 @@ +import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; +import { FormControl, Validators } from '@angular/forms'; +import { MatDialog } from '@angular/material/dialog'; +import { MatExpansionPanel } from '@angular/material/expansion'; +import { + ChoiceDto, + QuestionDto, + QuizzesApi, + SkillTestDto, +} from '@c4-soft/quiz-api'; +import { BehaviorSubject, Observable, debounceTime } from 'rxjs'; +import { ConfirmationDialog } from './confirmation.dialog'; +import { ErrorDialog } from './error.dialog'; + +@Component({ + selector: 'app-question-expansion-pannel', + template: ` + + {{ question.label }} + + done + close + + + Question + + + + + + +
+ + + +
+
+
+ + Comment + + +

+ {{ question.comment }} +

+ + +
+
`, + styles: [], + viewProviders: [MatExpansionPanel], +}) +export class QuestionExpansionPannelComponent implements OnInit { + @Input() + question!: QuestionDto; + + @Input() + expanded!: boolean; + + @Input() + isInEditMode$!: Observable; + + @Input() + isDragable!: boolean; + + @Input() + skillTest!: SkillTestDto; + + @Input() + isChoicesShuffled!: boolean; + + @Input() + isPerQuestionResult!: boolean; + + @Output() + onQuestionDeleted = new EventEmitter(); + + @Output() + onAnswerValidated = new EventEmitter(); + + @Output() + opened = new EventEmitter(); + + isValidated$ = new BehaviorSubject(false); + isAnswerCorrect?: boolean; + + labelCtrl = new FormControl('', [ + Validators.required, + Validators.minLength(1), + ]); + commentCtrl = new FormControl(''); + + questionAnswer = new Map(); + + constructor(private quizApi: QuizzesApi, private dialog: MatDialog) {} + + ngOnInit() { + if (this.isChoicesShuffled) { + if (!this.question.choices) { + this.question.choices = []; + } + this.question.choices.sort((a, b) => Math.random() - 0.5); + } + const answer = this.skillTest.questions.find( + (q) => q.questionId === this.question.questionId + ) || { questionId: this.question.questionId }; + (answer.choices || []).forEach((choiceId) => { + this.questionAnswer.set(choiceId, true); + }); + + this.commentCtrl.patchValue(this.question.comment); + this.labelCtrl.patchValue(this.question.label || ''); + this.commentCtrl.valueChanges + .pipe(debounceTime(500)) + .subscribe(() => this.updateQuestion()); + this.labelCtrl.valueChanges + .pipe(debounceTime(500)) + .subscribe(() => this.updateQuestion()); + } + + private updateQuestion() { + if (this.labelCtrl.valid && this.commentCtrl.valid) { + this.quizApi + .updateQuestion(this.question.quizId, this.question.questionId, { + label: this.labelCtrl.value || '', + comment: this.commentCtrl.value || '', + }) + .subscribe({ + next: () => { + this.question.comment = this.commentCtrl.value || ''; + this.question.label = this.labelCtrl.value || ''; + }, + error: (error) => { + this.dialog.open(ErrorDialog, { data: { error } }); + }, + }); + } + } + + delete() { + this.dialog + .open(ConfirmationDialog, { + data: { message: 'Delete question permanently?' }, + }) + .afterClosed() + .subscribe((isConfirmed) => { + if (!isConfirmed) { + return; + } + this.quizApi + .deleteQuestion(this.question.quizId, this.question.questionId) + .subscribe({ + next: () => { + this.onQuestionDeleted.emit(); + }, + error: (error) => { + this.dialog.open(ErrorDialog, { data: { error } }); + }, + }); + }); + } + + addChoice() { + this.question.choices = this.question.choices?.concat({ + quizId: this.question.quizId, + questionId: this.question.questionId, + choiceId: -1, + label: '', + isGood: false, + }); + } + + choiceDeleted(choice: ChoiceDto) { + this.question.choices = this.question.choices?.filter( + (c) => c.choiceId !== choice.choiceId + ); + } + + validateAnswer() { + const goodChoices = this.question.choices?.filter((c) => !!c.isGood) || []; + const answerChoices: number[] = []; + this.questionAnswer.forEach((isSelected: boolean, choiceId: number) => { + if (isSelected) { + answerChoices.push(choiceId); + } + }); + this.isAnswerCorrect = answerChoices.length === goodChoices.length; + goodChoices.forEach((c) => { + if (!answerChoices.includes(c.choiceId)) { + this.isAnswerCorrect = false; + } + }); + if (!this.skillTest.questions) { + this.skillTest.questions = []; + } + const existing = this.skillTest.questions.findIndex( + (q) => q.questionId === this.question.questionId + ); + if (existing > -1) { + this.skillTest.questions[existing].choices = answerChoices; + } else { + this.skillTest.questions.push({ + questionId: this.question.questionId, + choices: answerChoices, + }); + } + this.isValidated$.next(true); + this.onAnswerValidated.emit(); + } +} diff --git a/angular-ui/projects/quiz/src/app/quiz-creation.dialog.ts b/angular-ui/projects/quiz/src/app/quiz-creation.dialog.ts new file mode 100644 index 0000000..d5baaf9 --- /dev/null +++ b/angular-ui/projects/quiz/src/app/quiz-creation.dialog.ts @@ -0,0 +1,107 @@ +import { Component } from '@angular/core'; +import { FormControl, FormGroup, Validators } from '@angular/forms'; +import { MatDialog, MatDialogRef } from '@angular/material/dialog'; +import { QuizzesApi } from '@c4-soft/quiz-api'; +import { ErrorDialog } from './error.dialog'; + +@Component({ + selector: 'app-quiz-creation', + template: ` + New Quiz + +
+
+ + Title + + +
+ Shuffle choices +
+
+ Display answers after test is submitted +
+
+ Allow only one answer +
+
+ Skill-tests notifications by email +
+ +
+
`, + styles: [], +}) +export class QuizCreationDialog { + creationForm = new FormGroup({ + titleInput: new FormControl('', [Validators.required]), + isChoicesShuffled: new FormControl(true, []), + isNotPerQuestionResult: new FormControl(false, []), + isReplayDisabled: new FormControl(false, []), + isTrainerNotifiedOfNewTests: new FormControl(false, []), + }); + + constructor( + private dialogRef: MatDialogRef, + private quizApi: QuizzesApi, + private dialog: MatDialog + ) {} + + createQuiz() { + if (this.creationForm.invalid) { + return; + } + this.quizApi + .createQuiz( + { + title: this.creationForm.controls.titleInput.value || '', + isChoicesShuffled: + !!this.creationForm.controls.isChoicesShuffled.value, + isPerQuestionResult: + !this.creationForm.controls.isNotPerQuestionResult, + isReplayEnabled: !this.creationForm.controls.isReplayDisabled, + isTrainerNotifiedOfNewTests: + !!this.creationForm.controls.isTrainerNotifiedOfNewTests.value, + }, + 'response' + ) + .subscribe({ + next: (resp) => { + this.dialogRef.close(resp.headers.get('Location')); + }, + error: (error) => { + this.dialogRef.close(null); + this.dialog.open(ErrorDialog, { data: { error } }); + }, + }); + } +} diff --git a/angular-ui/projects/quiz/src/app/quiz-details.page.ts b/angular-ui/projects/quiz/src/app/quiz-details.page.ts new file mode 100644 index 0000000..1399124 --- /dev/null +++ b/angular-ui/projects/quiz/src/app/quiz-details.page.ts @@ -0,0 +1,531 @@ +import { CdkDragDrop, moveItemInArray } from '@angular/cdk/drag-drop'; +import { Component, OnDestroy, OnInit } from '@angular/core'; +import { FormControl, FormGroup, Validators } from '@angular/forms'; +import { MatDialog } from '@angular/material/dialog'; +import { ActivatedRoute, Router } from '@angular/router'; +import { + QuestionDto, + QuizDto, + QuizzesApi, + SkillTestApi, + SkillTestDto, +} from '@c4-soft/quiz-api'; +import { BehaviorSubject, Subscription } from 'rxjs'; +import { ErrorDialog } from './error.dialog'; +import { QuestionCreationDialog } from './question-creation.dialog'; +import { QuizRejectionDialog } from './quiz-rejection.dialog'; +import { SkillTestResultDialog } from './skill-test-result.dialog'; +import { UserService } from './user.service'; + +@Component({ + selector: 'app-quiz', + template: ` +
+
+ +
+
+
+ + Quiz title + + +
+
+ + + + + +
+

+ Authenticate before answering the test if you + intend to submit it. +

+

+ You already provided an answer to this quiz which currently accept only + one answer per trainee. Ask your trainer to delete your answer + before you try to submit a new one. +

+
+
+ Shuffle choices +
+
+ Answers after test is submitted +
+
+ One answer per trainee +
+
+ Skill-tests notifications by email +
+
+ + + +
+ + +
+
+
+ + +
+
`, + styles: [], +}) +export class QuizDetailsPage implements OnInit, OnDestroy { + isLoading: boolean = false; + quiz?: QuizDto; + focusedQuestion?: QuestionDto; + titleForm = new FormGroup({ + title: new FormControl('', [Validators.required]), + }); + quizOptionsForm = new FormGroup({ + isChoicesShuffled: new FormControl(true, []), + isNotPerQuestionResult: new FormControl(false, []), + isReplayDisabled: new FormControl(false, []), + isTrainerNotifiedOfNewTests: new FormControl(false, []), + }); + isInEditMode$ = new BehaviorSubject(false); + skillTest!: SkillTestDto; + isAnswerSubmitted: boolean = false; + + private titleFormSubscription?: Subscription; + private optionsFormSubscription?: Subscription; + private currentUserSubscription?: Subscription; + + constructor( + private quizApi: QuizzesApi, + private skillTestApi: SkillTestApi, + private activatedRoute: ActivatedRoute, + private router: Router, + private dialog: MatDialog, + private user: UserService + ) {} + + ngOnInit(): void { + this.loadQuiz(); + + this.activatedRoute.queryParamMap.subscribe((params) => { + this.isInEditMode$.next(params.get('edit') === 'true'); + }); + } + + ngOnDestroy(): void { + this.titleFormSubscription?.unsubscribe(); + this.optionsFormSubscription?.unsubscribe(); + this.currentUserSubscription?.unsubscribe(); + } + + get canEditQuiz(): boolean { + return ( + this.quiz?.authorName === this.user.current.name && !this.quiz.isReplaced + ); + } + + get canPublish(): boolean { + return this.user.current.isModerator; + } + + get isAuthenticated(): boolean { + return this.user.current.isAuthenticated; + } + + get isTestComplete(): boolean { + return this.skillTest.questions.length === this.quiz?.questions?.length; + } + + openQuestionCreationDialog() { + const dialogRef = this.dialog.open(QuestionCreationDialog, { + data: { quizId: this.quiz?.id }, + }); + dialogRef.afterClosed().subscribe((questionId) => { + this.loadQuiz(questionId); + }); + } + + setFocused(question?: QuestionDto) { + this.focusedQuestion = question; + } + + focusNextUnasweredQuestion() { + const nextUnanswered = this.quiz?.questions?.find((q) => { + const indexInAnswer = this.skillTest.questions.findIndex((answer) => { + return answer.questionId === q.questionId; + }); + return indexInAnswer < 0; + }); + this.setFocused(nextUnanswered); + } + + reorderQuestions(dragDrop: CdkDragDrop) { + if (!this.quiz) { + return; + } + moveItemInArray( + this.quiz.questions, + dragDrop.previousIndex, + dragDrop.currentIndex + ); + this.quizApi + .updateQuestionsOrder( + this.quiz.id, + this.quiz.questions.map((q) => q.questionId) + ) + .subscribe({ + error: (error) => this.dialog.open(ErrorDialog, { data: { error } }), + }); + } + + questionDeleted(question: QuestionDto) { + if (!!this.quiz) { + this.quiz.questions = this.quiz.questions.filter( + (q) => q.questionId !== question.questionId + ); + this.skillTest.questions = this.skillTest.questions.filter( + (q) => q.questionId !== question.questionId + ); + } + } + + toggleEditMode() { + if (this.quiz?.draftId) { + this.router.navigate(['/', 'quizzes', this.quiz?.draftId], { + queryParams: { edit: true }, + }); + } else if (this.quiz?.id) { + if (this.quiz.isPublished) { + this.quizApi.createDraft(this.quiz.id, 'response').subscribe({ + next: (response) => { + const draftId = response.headers.get('Location'); + if (!!draftId) { + this.isInEditMode$.next(true); + this.router.navigate(['/', 'quizzes', draftId], { + queryParams: { edit: true }, + }); + } else { + this.dialog.open(ErrorDialog, { + data: 'Failed to retrieve new draft ID.', + }); + } + }, + error: (error) => this.dialog.open(ErrorDialog, { data: error }), + }); + } else { + this.isInEditMode$.next(!this.isInEditMode$.value); + } + } + } + + submitDraft() { + if (!!this.quiz?.id) { + this.isLoading = true; + this.quizApi.submitDraft(this.quiz.id).subscribe({ + next: () => { + this.isLoading = false; + if (this.user.current.isModerator) { + this.toggleEditMode(); + } + this.loadQuiz(); + }, + error: (error) => { + this.isLoading = false; + this.dialog.open(ErrorDialog, { data: { error } }); + }, + }); + } + } + + publishDraft() { + if (!!this.quiz?.id) { + this.isLoading = true; + this.quizApi.publishDraft(this.quiz.id).subscribe({ + next: () => { + this.isLoading = false; + this.loadQuiz(); + }, + error: (error) => { + this.isLoading = false; + this.dialog.open(ErrorDialog, { data: { error } }); + }, + }); + } + } + + rejectDraft() { + const dialogRef = this.dialog.open(QuizRejectionDialog); + dialogRef.afterClosed().subscribe((message) => { + if (!!message) { + if (!!this.quiz?.id) { + this.isLoading = true; + this.quizApi.rejectDraft(this.quiz.id, { message: '' }).subscribe({ + next: () => { + this.isLoading = false; + this.loadQuiz(); + }, + error: (error) => { + this.isLoading = false; + this.dialog.open(ErrorDialog, { data: { error } }); + }, + }); + } + } + }); + } + + private loadQuiz(selectedQuestionId?: number) { + this.titleFormSubscription?.unsubscribe(); + this.optionsFormSubscription?.unsubscribe(); + this.activatedRoute.params.subscribe((parameter) => { + this.skillTest = { + quizId: parameter['quizId'], + questions: [], + }; + + this.isLoading = true; + this.quizApi.getQuiz(parameter['quizId']).subscribe({ + next: (dto) => { + this.isLoading = false; + this.quiz = dto; + this.titleForm.controls.title.patchValue(dto.title); + this.quizOptionsForm.controls.isChoicesShuffled.patchValue( + dto.isChoicesShuffled + ); + this.quizOptionsForm.controls.isNotPerQuestionResult.patchValue( + !dto.isPerQuestionResult + ); + this.quizOptionsForm.controls.isReplayDisabled.patchValue( + !dto.isReplayEnabled + ); + if (!!selectedQuestionId) { + this.setFocused( + this.quiz?.questions.find( + (q) => q.questionId === selectedQuestionId + ) || this.quiz?.questions?.at(0) + ); + } + if (!this.focusedQuestion) { + this.setFocused(this.quiz?.questions?.at(0)); + } + this.watchForms(); + }, + error: () => { + this.isLoading = false; + this.router.navigate(['/']); + }, + }); + + this.currentUserSubscription?.unsubscribe(); + this.currentUserSubscription = this.user.valueChanges.subscribe( + (currentUser) => { + if (currentUser.isAuthenticated) { + this.isLoading = true; + this.skillTestApi + .getSkillTest(parameter['quizId'], currentUser.name) + .subscribe({ + next: (dto) => { + this.isLoading = false; + this.isAnswerSubmitted = !!dto?.test?.quizId; + }, + error: () => { + this.isLoading = false; + this.isAnswerSubmitted = false; + }, + }); + } + } + ); + }); + } + + private watchForms() { + this.titleFormSubscription = this.titleForm.controls[ + 'title' + ].valueChanges.subscribe((title) => { + this.isLoading = true; + this.quizApi + .updateQuiz(this.quiz?.id || -1, { + title: title || this.quiz?.title || '', + isChoicesShuffled: this.quiz?.isChoicesShuffled || true, + isPerQuestionResult: this.quiz?.isPerQuestionResult || true, + isReplayEnabled: this.quiz?.isReplayEnabled || true, + isTrainerNotifiedOfNewTests: + this.quiz?.isTrainerNotifiedOfNewTests || false, + }) + .subscribe({ + next: () => { + this.isLoading = false; + if (!!this.quiz) { + this.quiz.title = title || ''; + } + }, + error: (error) => { + this.isLoading = false; + this.loadQuiz(); + this.dialog.open(ErrorDialog, { data: { error } }); + }, + }); + }); + + this.optionsFormSubscription = this.quizOptionsForm.valueChanges.subscribe( + (options) => { + this.isLoading = true; + this.quizApi + .updateQuiz(this.quiz?.id || -1, { + title: this.quiz?.title || '', + isChoicesShuffled: options.isChoicesShuffled === true, + isPerQuestionResult: !options.isNotPerQuestionResult, + isReplayEnabled: !options.isReplayDisabled, + isTrainerNotifiedOfNewTests: + options.isTrainerNotifiedOfNewTests || false, + }) + .subscribe({ + next: () => { + this.isLoading = false; + if (!!this.quiz) { + this.quiz.isChoicesShuffled = + options.isChoicesShuffled === true; + this.quiz.isPerQuestionResult = !options.isNotPerQuestionResult; + this.quiz.isReplayEnabled = !options.isReplayDisabled; + } + }, + error: (error) => { + this.isLoading = false; + this.loadQuiz(); + this.dialog.open(ErrorDialog, { data: { error } }); + }, + }); + } + ); + } + + submitAnswer() { + this.isLoading = true; + this.skillTestApi.submitSkillTest(this.skillTest).subscribe({ + next: (skillTest) => { + this.isLoading = false; + this.dialog.open(SkillTestResultDialog, { data: skillTest }); + this.isAnswerSubmitted = true; + }, + error: (error) => { + this.isLoading = false; + this.dialog.open(ErrorDialog, { data: { error } }); + }, + }); + } +} diff --git a/angular-ui/projects/quiz/src/app/quiz-rejection.dialog.ts b/angular-ui/projects/quiz/src/app/quiz-rejection.dialog.ts new file mode 100644 index 0000000..4a11fb0 --- /dev/null +++ b/angular-ui/projects/quiz/src/app/quiz-rejection.dialog.ts @@ -0,0 +1,41 @@ +import { Component } from '@angular/core'; +import { FormControl, FormGroup, Validators } from '@angular/forms'; +import { MatDialogRef } from '@angular/material/dialog'; + +@Component({ + selector: 'app-quiz-rejection', + template: ` +

You are about to reject a quiz draft

+
+ + Why do you reject? + + +
+ + + + + `, + styles: [], +}) +export class QuizRejectionDialog { + rejectionForm = new FormGroup({ + message: new FormControl('', [ + Validators.required, + Validators.minLength(3), + ]), + }); + + constructor(private dialog: MatDialogRef) {} + + cancel() { + this.dialog.close(); + } + + reject() { + if(this.rejectionForm.valid) { + this.dialog.close(this.rejectionForm.controls.message.value); + } + } +} diff --git a/angular-ui/projects/quiz/src/app/quiz-selection.page.ts b/angular-ui/projects/quiz/src/app/quiz-selection.page.ts new file mode 100644 index 0000000..c25f554 --- /dev/null +++ b/angular-ui/projects/quiz/src/app/quiz-selection.page.ts @@ -0,0 +1,200 @@ +import { Component, OnInit } from '@angular/core'; +import { MatDialog } from '@angular/material/dialog'; +import { Router } from '@angular/router'; +import { QuizDto, QuizzesApi } from '@c4-soft/quiz-api'; +import { QuizCreationDialog } from './quiz-creation.dialog'; +import { ConfirmationDialog } from './confirmation.dialog'; +import { UserService } from './user.service'; +import { ErrorDialog } from './error.dialog'; +import { FormControl, FormGroup } from '@angular/forms'; + +@Component({ + selector: 'app-quiz-selection', + template: ` +
+
+
+
+ + Author + + + + Title + + +
+
+ +
+
+ +
+ + + + + + + + +
`, + styles: [], +}) +export class QuizSelectionPage implements OnInit { + quizzes: QuizDto[] = []; + isLoading = true; + quizFilterForm = new FormGroup({ + author: new FormControl(''), + title: new FormControl(''), + }); + + constructor( + private quizApi: QuizzesApi, + private dialog: MatDialog, + private router: Router, + private user: UserService + ) {} + + ngOnInit(): void { + this.quizFilterForm.valueChanges.subscribe(() => { + this.loadQuizzes(); + }); + this.loadQuizzes(); + } + + get isAuthor(): boolean { + return this.user.current.isTrainer; + } + + private loadQuizzes() { + this.isLoading = true; + this.quizApi + .getQuizList( + this.quizFilterForm.controls.author.value || '', + this.quizFilterForm.controls.title.value || '' + ) + .subscribe({ + next: (quizzes) => { + this.quizzes = quizzes; + }, + error: () => { + this.quizzes = []; + }, + complete: () => { + this.isLoading = false; + }, + }); + } + + openQuizCreationDialog() { + const dialogRef = this.dialog.open(QuizCreationDialog); + dialogRef.afterClosed().subscribe(quizId => { + if(!!quizId) { + this.openQuizDetails(quizId, true) + } else { + this.loadQuizzes(); + } + }); + } + + openQuizDetails(quizId: number, isEditMode: boolean) { + this.router.navigate(['quizzes', quizId], { + queryParams: { edit: isEditMode }, + }); + } + + canDelete(quiz: QuizDto): boolean { + return ( + this.user.current.isModerator || + quiz?.authorName === this.user.current.name + ); + } + + deleteQuiz(quizId: number) { + this.dialog + .open(ConfirmationDialog, { + data: { message: 'Delete quiz permanently?' }, + }) + .afterClosed() + .subscribe({ + next: (isConfirmed) => { + if (!isConfirmed) { + return; + } + this.isLoading = true; + this.quizApi.deleteQuiz(quizId).subscribe({ + complete: () => { + this.isLoading = false; + this.loadQuizzes(); + }, + error: (error) => { + this.isLoading = false; + this.dialog.open(ErrorDialog, { data: { error } }); + }, + }); + }, + }); + } +} diff --git a/angular-ui/projects/quiz/src/app/skill-test-details.page.ts b/angular-ui/projects/quiz/src/app/skill-test-details.page.ts new file mode 100644 index 0000000..109bc2d --- /dev/null +++ b/angular-ui/projects/quiz/src/app/skill-test-details.page.ts @@ -0,0 +1,118 @@ +import { Component, OnInit } from '@angular/core'; +import { MatDialog } from '@angular/material/dialog'; +import { ActivatedRoute } from '@angular/router'; +import { + QuizDto, + QuizzesApi, + SkillTestApi, + SkillTestResultDetailsDto, +} from '@c4-soft/quiz-api'; +import { ErrorDialog } from './error.dialog'; + +@Component({ + selector: 'app-skill-test-details', + template: ` +
+
+ +
+

{{ skillTest?.traineeUsername }}: {{ skillTest?.score }}

+

{{ skillTest?.traineeFirstName }} {{ skillTest?.traineeLastName }} {{ skillTest?.traineeEmail }}

+
+
+
{{ question.label }}
+
+ + {{ choice.label }} + done + close +
+
+
+
`, + styles: [], +}) +export class SkillTestDetailsPage implements OnInit { + isQuizzesLoading: boolean = false + isSkillTestsLoading: boolean = false + quiz?: QuizDto; + skillTest?: SkillTestResultDetailsDto; + + constructor( + private quizzesApi: QuizzesApi, + private skillTestApi: SkillTestApi, + private activatedRoute: ActivatedRoute, + private dialog: MatDialog + ) {} + + ngOnInit(): void { + this.activatedRoute.params.subscribe((parameter) => { + this.isQuizzesLoading = true; + this.quizzesApi.getQuiz(parameter['quizId']).subscribe({ + next: (dto) => { + this.quiz = dto; + this.isQuizzesLoading = false; + }, + error: (error) => { + this.isQuizzesLoading = false; + this.dialog.open(ErrorDialog, { data: { error } }); + }, + }); + + this.isSkillTestsLoading = true + this.skillTestApi + .getSkillTest(parameter['quizId'], parameter['traineeName']) + .subscribe({ + next: (dto) => { + this.skillTest = dto; + this.isSkillTestsLoading = false + }, + error: (error) => { + this.isSkillTestsLoading = false + this.dialog.open(ErrorDialog, { data: { error } }); + }, + }); + }); + } + + get title(): string { + return this.quiz?.title || ''; + } + + get email(): string { + return `mailto:${this.skillTest?.traineeEmail}` + } + + isChoiceSelected(questionId: number, choiceId: number): boolean { + return ( + this.skillTest?.test.questions + .filter((q) => q.questionId === questionId) + .at(0) + ?.choices?.filter((c) => c === choiceId) + .at(0) !== undefined || false + ); + } +} diff --git a/angular-ui/projects/quiz/src/app/skill-test-result.dialog.ts b/angular-ui/projects/quiz/src/app/skill-test-result.dialog.ts new file mode 100644 index 0000000..ec9b4e2 --- /dev/null +++ b/angular-ui/projects/quiz/src/app/skill-test-result.dialog.ts @@ -0,0 +1,20 @@ +import { Component, Inject } from '@angular/core'; +import { MAT_DIALOG_DATA } from '@angular/material/dialog'; +import { SkillTestResultPreviewDto } from '@c4-soft/quiz-api'; + +@Component({ + selector: 'app-skill-test-result', + template: ` + Answer saved + +
+
Score: {{ data.score }}
+
Your trainer was notified
+
`, + styles: [ + ] +}) +export class SkillTestResultDialog { + + constructor(@Inject(MAT_DIALOG_DATA) public data: SkillTestResultPreviewDto) {} +} diff --git a/angular-ui/projects/quiz/src/app/skill-test-selection.page.ts b/angular-ui/projects/quiz/src/app/skill-test-selection.page.ts new file mode 100644 index 0000000..a76babc --- /dev/null +++ b/angular-ui/projects/quiz/src/app/skill-test-selection.page.ts @@ -0,0 +1,211 @@ +import { Component, OnInit } from '@angular/core'; +import { FormControl, FormGroup, Validators } from '@angular/forms'; +import { MatDialog } from '@angular/material/dialog'; +import { + QuizDto, + QuizzesApi, + SkillTestApi, + SkillTestResultPreviewDto, +} from '@c4-soft/quiz-api'; +import { ErrorDialog } from './error.dialog'; +import { UserService } from './user.service'; +import * as moment from 'moment'; +import { Router } from '@angular/router'; +import { ConfirmationDialog } from './confirmation.dialog'; + +@Component({ + selector: 'app-skill-test-selection', + template: ` +
+
+ +
+
+
+
+
+ + Quiz + + + {{ quiz.title }} + + + + + Skill tests date range + + + + + MM/DD/YYYY – MM/DD/YYYY + + + + + + + + +
+ +
+
+ + + + + + +
+
`, + styles: [], +}) +export class SkillTestSelectionPage implements OnInit { + isLoading: boolean = false; + now: Date = new Date(); + aWeekAgo: Date = new Date(Date.now() - 7 * 24 * 3600 * 1000); + skillTestFilterForm = new FormGroup({ + quizSelection: new FormControl(null, [Validators.required]), + from: new FormControl(moment(this.aWeekAgo.toUTCString())), + to: new FormControl(moment(this.now.toUTCString())), + }); + + quizzes: QuizDto[] = []; + skillTests: SkillTestResultPreviewDto[] = []; + + constructor( + private user: UserService, + private quizzesApi: QuizzesApi, + private skillTestApi: SkillTestApi, + private dialog: MatDialog, + private router: Router + ) {} + + ngOnInit(): void { + this.quizzesApi.getQuizList(this.user.current.name).subscribe({ + next: (dto) => { + this.quizzes = dto; + this.skillTestFilterForm.controls.quizSelection.patchValue(dto[0]?.id); + }, + error: (error) => { + this.dialog.open(ErrorDialog, { data: { error } }); + }, + }); + + this.skillTestFilterForm.valueChanges.subscribe((skillTestFilter) => { + if ( + skillTestFilter.quizSelection === null || + skillTestFilter.quizSelection === undefined + ) { + return; + } + this.skillTests = []; + console.log( + 'getSkillTestList', + skillTestFilter.quizSelection, + skillTestFilter.from?.unix(), + skillTestFilter.to?.unix() + ); + this.skillTestApi + .getSkillTestList( + skillTestFilter.quizSelection, + skillTestFilter.from?.unix(), + skillTestFilter.to?.unix() + ) + .subscribe({ + next: (dto) => { + this.skillTests = dto; + }, + error: (error) => { + this.dialog.open(ErrorDialog, { data: { error } }); + }, + }); + }); + } + + openDetails(traineeName: string) { + this.router.navigate([ + '/', + 'tests', + this.skillTestFilterForm.controls.quizSelection.value, + traineeName, + ]); + } + + deleteSkillTest(traineeName: string) { + this.dialog + .open(ConfirmationDialog, { + data: { message: 'Delete skill-test permanently?' }, + }) + .afterClosed() + .subscribe({ + next: (isConfirmed) => { + if (!isConfirmed) { + return; + } + + this.isLoading = true; + this.skillTestApi + .deleteSkillTest( + this.skillTestFilterForm.controls.quizSelection.value || -1, + traineeName + ) + .subscribe({ + complete: () => { + this.isLoading = false; + const idx = this.skillTests.findIndex( + (st) => st.traineeName === traineeName + ); + if (idx >= 0) { + this.skillTests.splice(idx, 1); + } + }, + error: (error) => { + this.isLoading = false; + this.dialog.open(ErrorDialog, { data: { error } }); + }, + }); + }, + }); + } +} diff --git a/angular-ui/projects/quiz/src/app/toolbar.component.ts b/angular-ui/projects/quiz/src/app/toolbar.component.ts index 8e20e77..2753439 100644 --- a/angular-ui/projects/quiz/src/app/toolbar.component.ts +++ b/angular-ui/projects/quiz/src/app/toolbar.component.ts @@ -1,6 +1,9 @@ -import { Component } from '@angular/core'; +import { Component, Input } from '@angular/core'; import { UserService } from './user.service'; import { BFFApi } from '@c4-soft/bff-api'; +import { MatDialog } from '@angular/material/dialog'; +import { QuizCreationDialog } from './quiz-creation.dialog'; +import { Router } from '@angular/router'; @Component({ selector: 'app-toolbar', @@ -10,6 +13,8 @@ import { BFFApi } from '@c4-soft/bff-api'; Quiz by ch4mpy + {{ title }} + - + + `, styles: [ ] }) export class ToolbarComponent { - constructor(private user: UserService, private bff: BFFApi) {} + + @Input() + title!: string + + constructor(private user: UserService, private bff: BFFApi, private dialog: MatDialog, private router: Router) {} get currentUser() { return this.user.current; @@ -50,4 +59,12 @@ export class ToolbarComponent { logout() { this.user.logout(); } + + openQuizSelectionPage() { + this.router.navigate(['/']) + } + + openSkillTestSelectionPage() { + this.router.navigate(['/', 'tests']) + } } diff --git a/angular-ui/projects/quiz/src/app/user.service.ts b/angular-ui/projects/quiz/src/app/user.service.ts index c4b5f4d..db26c3e 100644 --- a/angular-ui/projects/quiz/src/app/user.service.ts +++ b/angular-ui/projects/quiz/src/app/user.service.ts @@ -1,32 +1,38 @@ import { Injectable } from '@angular/core'; -import { HttpClient } from '@angular/common/http' import { BFFApi, LoginOptionDto } from '@c4-soft/bff-api'; +import { UsersApi } from '@c4-soft/quiz-api'; +import { Subscription, interval } from 'rxjs'; import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject'; -import { lastValueFrom } from 'rxjs/internal/lastValueFrom'; import { Observable } from 'rxjs/internal/Observable'; +import { lastValueFrom } from 'rxjs/internal/lastValueFrom'; @Injectable({ providedIn: 'root', }) export class UserService { private user$ = new BehaviorSubject(User.ANONYMOUS); + private refreshSub?: Subscription; - constructor(private bffApi: BFFApi, private http: HttpClient) { + constructor(private bffApi: BFFApi, private usersApi: UsersApi) { this.refresh(); } refresh(): void { - this.bffApi.getMe().subscribe({ + this.refreshSub?.unsubscribe(); + this.usersApi.getMe().subscribe({ next: (user) => { - console.info(user); this.user$.next( user.username - ? new User( - user.username, - user.roles || [] - ) + ? new User(user.username, user.roles || []) : User.ANONYMOUS ); + if (!!user.username) { + const now = Date.now(); + const delay = (1000 * user.exp - now) * 0.8; + if (delay > 2000) { + this.refreshSub = interval(delay).subscribe(() => this.refresh()); + } + } }, error: (error) => { console.warn(error); @@ -40,14 +46,16 @@ export class UserService { } async logout() { - lastValueFrom(this.bffApi.logout('response')).then((resp) => { - const logoutUri = resp.headers.get('location') || ''; - if (logoutUri) { - window.location.href = logoutUri; - } - }).finally(() => { - this.user$.next(User.ANONYMOUS) - }); + lastValueFrom(this.bffApi.logout('response')) + .then((resp) => { + const logoutUri = resp.headers.get('location') || ''; + if (logoutUri) { + window.location.href = logoutUri; + } + }) + .finally(() => { + this.user$.next(User.ANONYMOUS); + }); } get loginOptions(): Observable> { @@ -66,10 +74,7 @@ export class UserService { export class User { static readonly ANONYMOUS = new User('', []); - constructor( - readonly name: string, - readonly roles: string[] - ) {} + constructor(readonly name: string, readonly roles: string[]) {} get isAuthenticated(): boolean { return !!this.name; @@ -83,4 +88,12 @@ export class User { } return false; } + + get isTrainer(): boolean { + return this.hasAnyRole('trainer'); + } + + get isModerator(): boolean { + return this.hasAnyRole('moderator'); + } } diff --git a/angular-ui/projects/quiz/src/styles.scss b/angular-ui/projects/quiz/src/styles.scss index 09b15f3..54c148d 100644 --- a/angular-ui/projects/quiz/src/styles.scss +++ b/angular-ui/projects/quiz/src/styles.scss @@ -1,4 +1,7 @@ /* You can add global styles to this file, and also import other style files */ +@use '@angular/material' as mat; + +@include mat.core(); html, body { height: 100%; } body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; } @@ -6,3 +9,21 @@ body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; } .spacer { flex: 1 1 auto; } + +.item-button { + margin: 1em; +} + +.item-mini-button { + margin: .5em; +} + +.page-body { + width: 100%; + max-width: 1280px; + margin: auto; +} + +.warn-color { + color: mat.get-color-from-palette(mat.$red-palette, 500); +} diff --git a/api/bff/pom.xml b/api/bff/pom.xml index 2b19d96..bc85d51 100644 --- a/api/bff/pom.xml +++ b/api/bff/pom.xml @@ -13,13 +13,15 @@ - org.springframework.boot - spring-boot-starter-actuator + io.swagger.core.v3 + swagger-annotations org.springframework.cloud spring-cloud-starter-gateway + + org.springframework.boot spring-boot-starter-oauth2-client @@ -32,21 +34,40 @@ com.c4-soft.springaddons spring-addons-starter-oidc + + - io.swagger.core.v3 - swagger-annotations + org.springframework.boot + spring-boot-starter-actuator + + io.micrometer + micrometer-tracing + + + io.micrometer + micrometer-tracing-bridge-brave + + + org.springframework.boot spring-boot-devtools runtime true + + org.springframework.boot + spring-boot-configuration-processor + true + org.projectlombok lombok true + + org.springframework.boot spring-boot-starter-test diff --git a/api/bff/src/main/java/com/c4soft/quiz/BffApplication.java b/api/bff/src/main/java/com/c4soft/quiz/BffApplication.java index bcb1eb7..3708870 100644 --- a/api/bff/src/main/java/com/c4soft/quiz/BffApplication.java +++ b/api/bff/src/main/java/com/c4soft/quiz/BffApplication.java @@ -2,11 +2,18 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity; @SpringBootApplication public class BffApplication { public static void main(String[] args) { SpringApplication.run(BffApplication.class, args); + } + + @EnableReactiveMethodSecurity + @Configuration + public static class SecurityConfig { } } diff --git a/api/bff/src/main/java/com/c4soft/quiz/BffController.java b/api/bff/src/main/java/com/c4soft/quiz/BffController.java index a1b151e..bd3c480 100644 --- a/api/bff/src/main/java/com/c4soft/quiz/BffController.java +++ b/api/bff/src/main/java/com/c4soft/quiz/BffController.java @@ -3,26 +3,22 @@ import java.net.URI; import java.net.URISyntaxException; import java.util.List; -import java.util.Map; import org.springframework.boot.autoconfigure.security.oauth2.client.OAuth2ClientProperties; +import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.security.authentication.AnonymousAuthenticationToken; import org.springframework.security.core.Authentication; -import org.springframework.security.core.GrantedAuthority; import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; import org.springframework.security.oauth2.client.registration.ReactiveClientRegistrationRepository; import org.springframework.security.oauth2.core.oidc.user.OidcUser; -import org.springframework.security.web.server.context.ServerSecurityContextRepository; -import org.springframework.security.web.server.context.WebSessionServerSecurityContextRepository; +import org.springframework.security.web.server.WebFilterExchange; +import org.springframework.security.web.server.authentication.logout.ServerLogoutHandler; import org.springframework.util.StringUtils; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.server.ServerWebExchange; -import com.c4_soft.springaddons.security.oidc.OpenidClaimSet; import com.c4_soft.springaddons.security.oidc.starter.LogoutRequestUriBuilder; import com.c4_soft.springaddons.security.oidc.starter.properties.SpringAddonsOidcClientProperties; import com.c4_soft.springaddons.security.oidc.starter.properties.SpringAddonsOidcProperties; @@ -31,7 +27,6 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotNull; import reactor.core.publisher.Mono; @RestController @@ -40,31 +35,25 @@ public class BffController { private final ReactiveClientRegistrationRepository clientRegistrationRepository; private final SpringAddonsOidcClientProperties addonsClientProps; private final LogoutRequestUriBuilder logoutRequestUriBuilder; - private final ServerSecurityContextRepository securityContextRepository = new WebSessionServerSecurityContextRepository(); + private final ServerLogoutHandler logoutHandler; private final List loginOptions; public BffController( OAuth2ClientProperties clientProps, ReactiveClientRegistrationRepository clientRegistrationRepository, SpringAddonsOidcProperties addonsClientProps, - LogoutRequestUriBuilder logoutRequestUriBuilder) { + LogoutRequestUriBuilder logoutRequestUriBuilder, + ServerLogoutHandler logoutHandler) { this.addonsClientProps = addonsClientProps.getClient(); this.clientRegistrationRepository = clientRegistrationRepository; this.logoutRequestUriBuilder = logoutRequestUriBuilder; + this.logoutHandler = logoutHandler; this.loginOptions = clientProps.getRegistration().entrySet().stream().filter(e -> "authorization_code".equals(e.getValue().getAuthorizationGrantType())) - .map(e -> new LoginOptionDto(e.getValue().getProvider(), "%s/oauth2/authorization/%s".formatted(this.addonsClientProps.getClientUri(), e.getKey()))) + .map(e -> new LoginOptionDto(e.getValue().getProvider(), "/oauth2/authorization/%s".formatted(e.getKey()))) .toList(); } - @GetMapping(path = "/me", produces = "application/json") - public Mono getMe(Authentication auth) { - if(auth instanceof AnonymousAuthenticationToken) { - return Mono.just(UserInfoDto.ANONYMOUS); - } - return Mono.just(new UserInfoDto(auth.getName(), auth.getAuthorities().stream().map(GrantedAuthority::getAuthority).toList())); - } - - @GetMapping(path = "/login/options", produces = "application/json") + @GetMapping(path = "/login/options", produces = MediaType.APPLICATION_JSON_VALUE) @Operation(operationId = "getLoginOptions") public Mono> getLoginOptions(Authentication auth) throws URISyntaxException { final boolean isAuthenticated = auth instanceof OAuth2AuthenticationToken; @@ -85,7 +74,7 @@ public Mono> logout(ServerWebExchange exchange, Authenticat uri = Mono.just(addonsClientProps.getPostLogoutRedirectUri()); } return uri.flatMap(logoutUri -> { - return securityContextRepository.save(exchange, null).thenReturn(logoutUri); + return logoutHandler.logout(new WebFilterExchange(exchange, ex -> Mono.empty().then()), authentication).thenReturn(logoutUri); }).map(logoutUri -> { return ResponseEntity.noContent().location(logoutUri).build(); }); @@ -93,8 +82,4 @@ public Mono> logout(ServerWebExchange exchange, Authenticat public static record LoginOptionDto(@NotEmpty String label, @NotEmpty String loginUri) { } - - public static record UserInfoDto(@NotNull String username, @NotNull List roles) { - public static final UserInfoDto ANONYMOUS = new UserInfoDto("", List.of()); - } } diff --git a/api/bff/src/main/resources/application.yml b/api/bff/src/main/resources/application.yml index 09beef9..41e3370 100644 --- a/api/bff/src/main/resources/application.yml +++ b/api/bff/src/main/resources/application.yml @@ -1,6 +1,6 @@ scheme: http -oauth2-issuer: https://oidc.c4-soft.com/auth/realms/master -oauth2-client-id: quiz +oauth2-issuer: https://oidc.c4-soft.com/auth/realms/quiz +oauth2-client-id: quiz-bff oauth2-client-secret: change-me gateway-uri: ${scheme}://localhost:${server.port} @@ -56,14 +56,21 @@ spring: predicates: - Path=/ui/** # Access the quiz API with BFF pattern - - id: quiz-api-bff + - id: quiz-bff uri: ${quiz-api-uri} predicates: - - Path=/bff/quizzes/v1/** + - Path=/bff/v1/** filters: - TokenRelay= - SaveSession - StripPrefix=2 + - id: quiz-resource-server + uri: ${quiz-api-uri} + predicates: + - Path=/resource-server/v1/** + filters: + - SaveSession + - StripPrefix=2 # Cert-manager http01 challenge for SSL certificates on K8s - id: letsencrypt uri: https://cert-manager-webhook @@ -77,12 +84,10 @@ com: # Global OAuth2 configuration ops: - iss: ${oauth2-issuer} - username-claim: $.preferred_username + username-claim: ${spring.security.oauth2.client.provider.oauth2.user-name-attribute} authorities: - path: $.realm_access.roles - - path: $.resource_access.${oauth2-client-id}.roles client: - cors: client-uri: ${gateway-uri} security-matchers: - /login/** @@ -90,28 +95,26 @@ com: - / - /me - /logout - - /bff/v1/** + - /bff/** permit-all: - /login/** - /oauth2/** - / - /me - - /bff/v1/** + - /bff/** csrf: cookie-accessible-from-js post-login-redirect-path: /ui post-logout-redirect-path: /ui - back-channel-logout-enabled: true # OAuth2 resource server configuration resourceserver: - cors: permit-all: - /ui/** - - /resource-server/v1/** + - /resource-server/** - /v3/api-docs/** - /actuator/health/readiness - /actuator/health/liveness - /.well-known/acme-challenge/** - + management: endpoint: health: @@ -133,7 +136,7 @@ logging: org: springframework: security: INFO - boot: DEBUG + boot: INFO --- spring: @@ -143,7 +146,7 @@ spring: cloud: gateway: default-filters: - - DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin + - DedupeResponseHeader=Access-Control-Allow-Credentials Access-Control-Allow-Origin Access-Control-Request-Method Access-Control-Request-Headers - SecureHeaders server: ssl: diff --git a/api/bff/src/main/resources/banner.txt b/api/bff/src/main/resources/banner.txt index be1674a..6a101a8 100644 --- a/api/bff/src/main/resources/banner.txt +++ b/api/bff/src/main/resources/banner.txt @@ -4,17 +4,11 @@ | | |__ _||______| \___ \ / _ \ | _|| __| / __| / _ \ | '_ ` _ \ | |____ | | ____) || (_) || | | |_ _ | (__ | (_) || | | | | | \_____| |_| |_____/ \___/ |_| \__|(_) \___| \___/ |_| |_| |_| - ____ _____ _____ _______ _ _ - / __ \ |_ _|| __ \ |__ __| (_) (_) -| | | | _ __ ___ _ __ | | | | | | | | _ __ __ _ _ _ __ _ _ __ __ _ -| | | || '_ \ / _ \| '_ \ | | | | | | | | | '__| / _` || || '_ \ | || '_ \ / _` | -| |__| || |_) || __/| | | | _| |_ | |__| | | | | | | (_| || || | | || || | | || (_| | - \____/ | .__/ \___||_| |_||_____||_____/ |_| |_| \__,_||_||_| |_||_||_| |_| \__, | - | | __/ | - |_| |___/ - ____ ______ ______ -| _ \ | ____|| ____| -| |_) || |__ | |__ -| _ < | __| | __| -| |_) || | | | -|____/ |_| |_| + ____ _ ____ ______ ______ + / __ \ (_) | _ \| ____| ____| + | | | |_ _ _ ____ | |_) | |__ | |__ + | | | | | | | |_ / | _ <| __| | __| + | |__| | |_| | |/ / | |_) | | | | + \___\_\\__,_|_/___| |____/|_| |_| + + \ No newline at end of file diff --git a/api/pom.xml b/api/pom.xml index 46c7c48..d0d0004 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -6,7 +6,7 @@ org.springframework.boot spring-boot-starter-parent - 3.1.1 + 3.1.4 @@ -16,13 +16,13 @@ pom - 17 - 2022.0.3 - 7.0.1 + 21 + 2022.0.4 + 7.1.10 0.2.0 1.5.5.Final - 2.2.10 - 2.1.0 + 2.2.15 + 2.2.0 1.4 ${project.basedir}/../../angular-ui http @@ -43,7 +43,7 @@ org.springframework.cloud spring-cloud-dependencies - ${spring-cloud.version} + ${spring-cloud-dependencies.version} pom import @@ -123,6 +123,11 @@ mapstruct-processor ${org.mapstruct.version} + + org.hibernate + hibernate-jpamodelgen + ${hibernate.version} + diff --git a/api/quiz-api/pom.xml b/api/quiz-api/pom.xml index 32ca822..175f6f4 100644 --- a/api/quiz-api/pom.xml +++ b/api/quiz-api/pom.xml @@ -13,8 +13,8 @@ - org.springframework.boot - spring-boot-starter-actuator + io.swagger.core.v3 + swagger-annotations org.springframework.boot @@ -22,7 +22,7 @@ org.springframework.boot - spring-boot-starter-oauth2-resource-server + spring-boot-starter-mail org.springframework.boot @@ -33,20 +33,35 @@ spring-boot-starter-web - com.h2database - h2 - test + org.springframework.cloud + spring-cloud-starter-openfeign + + + org.springframework.boot + spring-boot-starter-oauth2-resource-server + com.c4-soft.springaddons spring-addons-starter-oidc + + - io.swagger.core.v3 - swagger-annotations + org.springframework.boot + spring-boot-starter-actuator + + + io.micrometer + micrometer-tracing + + + io.micrometer + micrometer-tracing-bridge-brave + org.springframework.boot spring-boot-devtools @@ -63,6 +78,13 @@ lombok true + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + org.springframework.boot spring-boot-starter-test @@ -73,6 +95,11 @@ spring-addons-starter-oidc-test test + + com.h2database + h2 + test + diff --git a/api/quiz-api/src/main/java/com/c4soft/quiz/SecurityConfig.java b/api/quiz-api/src/main/java/com/c4soft/quiz/SecurityConfig.java index 436f66f..53a90f0 100644 --- a/api/quiz-api/src/main/java/com/c4soft/quiz/SecurityConfig.java +++ b/api/quiz-api/src/main/java/com/c4soft/quiz/SecurityConfig.java @@ -10,22 +10,22 @@ import org.springframework.security.core.GrantedAuthority; import org.springframework.security.oauth2.jwt.JwtClaimNames; -import com.c4_soft.springaddons.security.oidc.OAuthentication; import com.c4_soft.springaddons.security.oidc.OpenidClaimSet; import com.c4_soft.springaddons.security.oidc.starter.properties.SpringAddonsOidcProperties; import com.c4_soft.springaddons.security.oidc.starter.synchronised.resourceserver.JwtAbstractAuthenticationTokenConverter; +import com.c4soft.quiz.domain.QuizAuthentication; @Configuration @EnableMethodSecurity() public class SecurityConfig { - @Bean - JwtAbstractAuthenticationTokenConverter authenticationFactory( - Converter, Collection> authoritiesConverter, - SpringAddonsOidcProperties addonsProperties) { - return jwt -> { - final var opProperties = addonsProperties.getOpProperties(jwt.getClaims().get(JwtClaimNames.ISS)); - final var claims = new OpenidClaimSet(jwt.getClaims(), opProperties.getUsernameClaim()); - return new OAuthentication<>(claims, authoritiesConverter.convert(claims), jwt.getTokenValue()); - }; - } + @Bean + JwtAbstractAuthenticationTokenConverter authenticationFactory( + Converter, Collection> authoritiesConverter, + SpringAddonsOidcProperties addonsProperties) { + return jwt -> { + final var opProperties = addonsProperties.getOpProperties(jwt.getClaims().get(JwtClaimNames.ISS)); + final var claims = new OpenidClaimSet(jwt.getClaims(), opProperties.getUsernameClaim()); + return new QuizAuthentication(claims, authoritiesConverter.convert(claims), jwt.getTokenValue()); + }; + } } diff --git a/api/quiz-api/src/main/java/com/c4soft/quiz/domain/Choice.java b/api/quiz-api/src/main/java/com/c4soft/quiz/domain/Choice.java index ecdc061..70a56cd 100644 --- a/api/quiz-api/src/main/java/com/c4soft/quiz/domain/Choice.java +++ b/api/quiz-api/src/main/java/com/c4soft/quiz/domain/Choice.java @@ -17,6 +17,11 @@ public Choice(String label, Boolean isGood) { this.label = label; this.isGood = isGood; } + + public Choice(Choice other) { + this.label = other.label; + this.isGood = other.isGood; + } @Id @GeneratedValue @@ -24,7 +29,7 @@ public Choice(String label, Boolean isGood) { @ManyToOne(optional = false) @JoinColumn(name = "question_id", updatable = false, nullable = false) - Question question; + private Question question; @Column private String label; diff --git a/api/quiz-api/src/main/java/com/c4soft/quiz/domain/DraftAlreadyExistsException.java b/api/quiz-api/src/main/java/com/c4soft/quiz/domain/DraftAlreadyExistsException.java new file mode 100644 index 0000000..0b918d2 --- /dev/null +++ b/api/quiz-api/src/main/java/com/c4soft/quiz/domain/DraftAlreadyExistsException.java @@ -0,0 +1,15 @@ +package com.c4soft.quiz.domain; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ResponseStatus(HttpStatus.CONFLICT) +public class DraftAlreadyExistsException extends RuntimeException { + private static final long serialVersionUID = -8566841487060787834L; + + public DraftAlreadyExistsException(Long quizId) { + super("A draft already exists for quiz %d".formatted(quizId)); + } + + +} diff --git a/api/quiz-api/src/main/java/com/c4soft/quiz/domain/NotADraftException.java b/api/quiz-api/src/main/java/com/c4soft/quiz/domain/NotADraftException.java new file mode 100644 index 0000000..b02ae6f --- /dev/null +++ b/api/quiz-api/src/main/java/com/c4soft/quiz/domain/NotADraftException.java @@ -0,0 +1,15 @@ +package com.c4soft.quiz.domain; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ResponseStatus(HttpStatus.CONFLICT) +public class NotADraftException extends RuntimeException { + private static final long serialVersionUID = -8566841487060787834L; + + public NotADraftException(Long quizId) { + super("Quiz %d is not a draft".formatted(quizId)); + } + + +} diff --git a/api/quiz-api/src/main/java/com/c4soft/quiz/domain/Question.java b/api/quiz-api/src/main/java/com/c4soft/quiz/domain/Question.java index 9d8225c..ebc3aec 100644 --- a/api/quiz-api/src/main/java/com/c4soft/quiz/domain/Question.java +++ b/api/quiz-api/src/main/java/com/c4soft/quiz/domain/Question.java @@ -30,6 +30,17 @@ public Question(String label, Integer priority, String comment, Choice... choice this.add(c); } } + + public Question(Question other) { + this.comment = other.comment; + this.label = other.label; + this.priority = other.priority; + this.choices = new ArrayList<>(other.choices.size()); + for(var c : other.choices) { + final var choice = new Choice(c); + this.add(choice); + } + } @Id @GeneratedValue @@ -37,7 +48,7 @@ public Question(String label, Integer priority, String comment, Choice... choice @ManyToOne(optional = false) @JoinColumn(name = "quiz_id", updatable = false, nullable = false) - Quiz quiz; + private Quiz quiz; @Column private String label; @@ -49,7 +60,7 @@ public Question(String label, Integer priority, String comment, Choice... choice @OneToMany(mappedBy = "question", cascade = CascadeType.ALL, orphanRemoval = true) private List choices = new ArrayList<>(); - @Column + @Column(length = 2048) private String comment; public List getChoices() { diff --git a/api/quiz-api/src/main/java/com/c4soft/quiz/domain/Quiz.java b/api/quiz-api/src/main/java/com/c4soft/quiz/domain/Quiz.java index 94a9b13..3ecef6c 100644 --- a/api/quiz-api/src/main/java/com/c4soft/quiz/domain/Quiz.java +++ b/api/quiz-api/src/main/java/com/c4soft/quiz/domain/Quiz.java @@ -1,7 +1,6 @@ package com.c4soft.quiz.domain; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import jakarta.persistence.CascadeType; @@ -10,6 +9,7 @@ import jakarta.persistence.GeneratedValue; import jakarta.persistence.Id; import jakarta.persistence.OneToMany; +import jakarta.persistence.OneToOne; import lombok.AccessLevel; import lombok.Data; import lombok.NoArgsConstructor; @@ -20,20 +20,33 @@ @NoArgsConstructor public class Quiz { - public Quiz(String title, String formerName, Question... questions) { + public Quiz(String title, String authorName, Question... questions) { this.title = title; - this.formerName = formerName; + this.authorName = authorName; this.questions = new ArrayList<>(questions.length); for (var q : questions) { this.add(q); } } + public Quiz(Quiz other, String authorName) { + this.authorName = authorName; + this.questions = new ArrayList<>(other.questions.size()); + for (var q : other.questions) { + final var question = new Question(q); + this.add(question); + } + this.isPublished = false; + this.moderatedBy = null; + this.replaces = other; + this.title = other.title; + } + @Id @GeneratedValue private Long id; - @Column(unique = true, nullable = false) + @Column(nullable = false) private String title; @Setter(AccessLevel.NONE) @@ -41,11 +54,44 @@ public Quiz(String title, String formerName, Question... questions) { private List questions = new ArrayList<>(); @Column(nullable = false, updatable = false) - private String formerName; + private String authorName; - public List getQuestions() { - return Collections.unmodifiableList(this.questions); - } + @Column(nullable = false) + private Boolean isSubmitted = false; + + @Column(nullable = false) + private Boolean isPublished = false; + + @OneToOne(optional = true) + private Quiz draft; + + @OneToOne(optional = true) + private Quiz replaces; + + @OneToOne(optional = true) + private Quiz replacedBy; + + @Column() + private String moderatorComment; + + @Column() + private String moderatedBy; + + @Column(nullable = false, columnDefinition = "boolean default true") + private Boolean isChoicesShuffled = true; + + @Column(nullable = false, columnDefinition = "boolean default true") + private Boolean isReplayEnabled = true; + + @Column(nullable = false, columnDefinition = "boolean default true") + private Boolean isPerQuestionResult = true; + + @Column(nullable = false, columnDefinition = "boolean default false") + private Boolean isTrainerNotifiedOfNewTests = false; + + @Setter(AccessLevel.NONE) + @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true) + private List skillTests = new ArrayList<>(); public Question getQuestion(Long questionId) { if(questionId == null) { diff --git a/api/quiz-api/src/main/java/com/c4soft/quiz/domain/QuizAuthentication.java b/api/quiz-api/src/main/java/com/c4soft/quiz/domain/QuizAuthentication.java new file mode 100644 index 0000000..9caaab3 --- /dev/null +++ b/api/quiz-api/src/main/java/com/c4soft/quiz/domain/QuizAuthentication.java @@ -0,0 +1,31 @@ +package com.c4soft.quiz.domain; + +import java.util.Collection; +import java.util.stream.Collectors; + +import org.springframework.security.core.GrantedAuthority; + +import com.c4_soft.springaddons.security.oidc.OAuthentication; +import com.c4_soft.springaddons.security.oidc.OpenidClaimSet; + +import lombok.Getter; + +@Getter +public class QuizAuthentication extends OAuthentication { + private static final long serialVersionUID = 1L; + public static final String AUTHORITY_MODERATOR = "moderator"; + public static final String AUTHORITY_TRAINER = "trainer"; + public static final String SPEL_IS_MODERATOR_OR_TRAINER = "hasAnyAuthority('moderator', 'trainer')"; + public static final String SPEL_IS_MODERATOR = "hasAuthority('moderator')"; + public static final String SPEL_IS_TRAINER = "hasAuthority('trainer')"; + + private final boolean isModerator; + private final boolean isTrainer; + + public QuizAuthentication(OpenidClaimSet claims, Collection authorities, String tokenString) { + super(claims, authorities, tokenString); + final var authoritiesStrings = authorities.stream().map(GrantedAuthority::getAuthority).collect(Collectors.toSet()); + this.isModerator = authoritiesStrings.contains(AUTHORITY_MODERATOR); + this.isTrainer = authoritiesStrings.contains(AUTHORITY_TRAINER); + } +} diff --git a/api/quiz-api/src/main/java/com/c4soft/quiz/domain/QuizRejectionDto.java b/api/quiz-api/src/main/java/com/c4soft/quiz/domain/QuizRejectionDto.java new file mode 100644 index 0000000..42df467 --- /dev/null +++ b/api/quiz-api/src/main/java/com/c4soft/quiz/domain/QuizRejectionDto.java @@ -0,0 +1,7 @@ +package com.c4soft.quiz.domain; + +import jakarta.validation.constraints.NotEmpty; + +public record QuizRejectionDto(@NotEmpty(message = "A non-empty message is mandatory") String message) { + +} diff --git a/api/quiz-api/src/main/java/com/c4soft/quiz/domain/QuizRepository.java b/api/quiz-api/src/main/java/com/c4soft/quiz/domain/QuizRepository.java index 9859d09..3b1d580 100644 --- a/api/quiz-api/src/main/java/com/c4soft/quiz/domain/QuizRepository.java +++ b/api/quiz-api/src/main/java/com/c4soft/quiz/domain/QuizRepository.java @@ -1,6 +1,47 @@ package com.c4soft.quiz.domain; +import java.util.List; +import java.util.Optional; + +import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; +import org.springframework.security.core.context.SecurityContextHolder; + +public interface QuizRepository extends JpaRepository, JpaSpecificationExecutor { + + Optional findByReplacesId(Long replacedQuizId); + + List findByIsSubmitted(boolean isSubmitted); + + static Specification searchSpec(Optional authorName, Optional quizTitle) { + final var currentUserName = SecurityContextHolder.getContext().getAuthentication().getName(); + var spec = Specification.where(isPublished()).or(authoredBy(currentUserName)); + if(authorName.isPresent()) { + spec = spec.and(authoredBy(authorName.get())); + } + if(quizTitle.isPresent()) { + spec = spec.and(titleContains(quizTitle.get())); + } + return orderByIdDesc(spec); + } + + static Specification isPublished() { + return (root, query, cb) -> cb.isTrue(root.get(Quiz_.isPublished)); + } + + static Specification authoredBy(String authorName) { + return (root, query, cb) -> cb.like(cb.upper(root.get(Quiz_.authorName)), "%%%s%%".formatted(authorName.toUpperCase())); + } + + static Specification titleContains(String title) { + return (root, query, cb) -> cb.like(cb.upper(root.get(Quiz_.title)), "%%%s%%".formatted(title.toUpperCase())); + } -public interface QuizRepository extends JpaRepository { + static Specification orderByIdDesc(Specification spec) { + return (root, query, cb) -> { + query.orderBy(cb.desc(root.get(Quiz_.id))); + return spec.toPredicate(root, query, cb); + }; + } } diff --git a/api/quiz-api/src/main/java/com/c4soft/quiz/domain/SkillTest.java b/api/quiz-api/src/main/java/com/c4soft/quiz/domain/SkillTest.java index 69b8a82..05e0fc7 100644 --- a/api/quiz-api/src/main/java/com/c4soft/quiz/domain/SkillTest.java +++ b/api/quiz-api/src/main/java/com/c4soft/quiz/domain/SkillTest.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.Objects; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.ResponseStatus; @@ -14,6 +15,7 @@ import jakarta.persistence.Entity; import jakarta.persistence.ManyToMany; import lombok.AccessLevel; +import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import lombok.Setter; @@ -22,53 +24,52 @@ @Data @NoArgsConstructor public class SkillTest { - public SkillTest(String traineeName, Collection choices) { - this.submittedOn = Instant.now().toEpochMilli(); - this.choices = new ArrayList<>(choices); - this.id.traineeName = traineeName; - final var quizIds = this.choices.stream().map(c -> c.getQuestion().getQuiz().getId()).distinct().toList(); - if(quizIds.size() == 0) { - throw new NotAcceptableSkillTestException("A skill test must contain at least one choice."); + public SkillTest(Quiz quiz, String traineeName, Collection choices) { + this.submittedOn = Instant.now().toEpochMilli(); + this.choices = new ArrayList<>(choices); + this.id.traineeName = traineeName; + this.choices.forEach(c -> { + if (c.getQuestion() == null || c.getQuestion().getQuiz() == null || !Objects.equals(c.getQuestion().getQuiz().getId(), quiz.getId())) { + throw new NotAcceptableSkillTestException("All choices must target the same quiz (%s)".formatted(quiz.getTitle())); + } + }); + this.id.quizId = quiz.getId(); } - if(quizIds.size() > 1) { - throw new NotAcceptableSkillTestException("All choices of test must belong to questions from the same test."); - } - this.id.quizId = quizIds.get(0); - } - @Setter(AccessLevel.NONE) - @EmbeddedId - SkillTestPk id = new SkillTestPk(); + @Setter(AccessLevel.NONE) + @EmbeddedId + private final SkillTestPk id = new SkillTestPk(); - @Column - private Long submittedOn; + @Column + private Long submittedOn; - @ManyToMany - private List choices = new ArrayList<>(); + @ManyToMany + private List choices = new ArrayList<>(); - public List getChoices(Long questionId) { - return choices.stream().filter(c -> c.getQuestion().getId() == questionId).toList(); - } + public List getChoices(Long questionId) { + return choices.stream().filter(c -> Objects.equals(c.getQuestion().getId(), questionId)).toList(); + } - @ResponseStatus(HttpStatus.NOT_ACCEPTABLE) - static class NotAcceptableSkillTestException extends RuntimeException { - private static final long serialVersionUID = -6754084213295394103L; + @ResponseStatus(HttpStatus.NOT_ACCEPTABLE) + static class NotAcceptableSkillTestException extends RuntimeException { + private static final long serialVersionUID = -6754084213295394103L; - public NotAcceptableSkillTestException(String message) { - super(message); + public NotAcceptableSkillTestException(String message) { + super(message); + } } - } - - @Embeddable - @Data - @NoArgsConstructor - public static class SkillTestPk { - @Setter(AccessLevel.NONE) - @Column - private Long quizId; - @Setter(AccessLevel.NONE) - @Column - private String traineeName; - } + @Embeddable + @Data + @NoArgsConstructor + @AllArgsConstructor + public static class SkillTestPk { + @Setter(AccessLevel.NONE) + @Column + private Long quizId; + + @Setter(AccessLevel.NONE) + @Column + private String traineeName; + } } diff --git a/api/quiz-api/src/main/java/com/c4soft/quiz/domain/SkillTestRepository.java b/api/quiz-api/src/main/java/com/c4soft/quiz/domain/SkillTestRepository.java index dbffc27..4e770db 100644 --- a/api/quiz-api/src/main/java/com/c4soft/quiz/domain/SkillTestRepository.java +++ b/api/quiz-api/src/main/java/com/c4soft/quiz/domain/SkillTestRepository.java @@ -8,11 +8,15 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; -public interface SkillTestRepository extends JpaRepository, JpaSpecificationExecutor { +import com.c4soft.quiz.domain.SkillTest.SkillTestPk; + +public interface SkillTestRepository extends JpaRepository, JpaSpecificationExecutor { Optional findByIdQuizIdAndIdTraineeName(Long quizId, String traineeName); List findByIdQuizId(Long quizId); + void deleteByIdQuizId(Long quizId); + static Specification quizIdSpec(Long quizId) { return (skillTest, cq, cb) -> cb.equal(skillTest.get("id").get("quizId"), quizId); } @@ -29,11 +33,14 @@ static Specification untilSpec(Long until) { return (skillTest, cq, cb) -> cb.le(skillTest.get("submittedOn"), until); } - static Specification spec(Long quizId, Long since, Optional until, Optional traineeName) { - final var spec = Specification.where(quizIdSpec(quizId)); - spec.and(sinceSpec(since)); - spec.and(untilSpec(until.orElse(Instant.now().toEpochMilli()))); - traineeName.ifPresent(n -> spec.and(traineeNameSpec(n))); - return spec; + static Specification orderByIdDesc(Specification spec) { + return (root, query, cb) -> { + query.orderBy(cb.desc(root.get(SkillTest_.id))); + return spec.toPredicate(root, query, cb); + }; + } + + static Specification spec(Long quizId, Long since, Optional until) { + return orderByIdDesc(Specification.where(quizIdSpec(quizId)).and(sinceSpec(since)).and(untilSpec(until.orElse(Instant.now().toEpochMilli())))); } } diff --git a/api/quiz-api/src/main/java/com/c4soft/quiz/feign/FeignConfig.java b/api/quiz-api/src/main/java/com/c4soft/quiz/feign/FeignConfig.java new file mode 100644 index 0000000..1683270 --- /dev/null +++ b/api/quiz-api/src/main/java/com/c4soft/quiz/feign/FeignConfig.java @@ -0,0 +1,9 @@ +package com.c4soft.quiz.feign; + +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.context.annotation.Configuration; + +@Configuration +@EnableFeignClients +public class FeignConfig { +} diff --git a/api/quiz-api/src/main/java/com/c4soft/quiz/feign/KeycloakAdminApiClient.java b/api/quiz-api/src/main/java/com/c4soft/quiz/feign/KeycloakAdminApiClient.java new file mode 100644 index 0000000..11a7d8e --- /dev/null +++ b/api/quiz-api/src/main/java/com/c4soft/quiz/feign/KeycloakAdminApiClient.java @@ -0,0 +1,41 @@ +package com.c4soft.quiz.feign; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; + +@FeignClient(name = "keycloak-admin-api") +public interface KeycloakAdminApiClient { + @GetMapping(value = "/users") + List getUser(@RequestParam(value="username") String username, @RequestParam(value="exact") boolean exact); + + public static class UserRepresentation extends HashMap { + + private static final long serialVersionUID = -2288285379516753557L; + + public UserRepresentation() { + super(); + } + + public UserRepresentation(Map m) { + super(m); + } + + public String getEmail() { + return Optional.ofNullable(this.get("email")).map(Object::toString).orElse(null); + } + + public String getFirtsName() { + return Optional.ofNullable(this.get("firstName")).map(Object::toString).orElse(null); + } + + public String getLastName() { + return Optional.ofNullable(this.get("lastName")).map(Object::toString).orElse(null); + } + } +} \ No newline at end of file diff --git a/api/quiz-api/src/main/java/com/c4soft/quiz/web/QuizController.java b/api/quiz-api/src/main/java/com/c4soft/quiz/web/QuizController.java index b493733..6238b98 100644 --- a/api/quiz-api/src/main/java/com/c4soft/quiz/web/QuizController.java +++ b/api/quiz-api/src/main/java/com/c4soft/quiz/web/QuizController.java @@ -2,10 +2,13 @@ import java.net.URI; import java.util.List; +import java.util.Objects; +import java.util.Optional; import java.util.stream.Collectors; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.Authentication; @@ -18,14 +21,20 @@ import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import com.c4soft.quiz.domain.Choice; import com.c4soft.quiz.domain.ChoiceRepository; +import com.c4soft.quiz.domain.DraftAlreadyExistsException; +import com.c4soft.quiz.domain.NotADraftException; import com.c4soft.quiz.domain.Question; import com.c4soft.quiz.domain.QuestionRepository; import com.c4soft.quiz.domain.Quiz; +import com.c4soft.quiz.domain.QuizAuthentication; +import com.c4soft.quiz.domain.QuizRejectionDto; import com.c4soft.quiz.domain.QuizRepository; +import com.c4soft.quiz.domain.SkillTestRepository; import com.c4soft.quiz.web.dto.ChoiceDto; import com.c4soft.quiz.web.dto.ChoiceUpdateDto; import com.c4soft.quiz.web.dto.QuestionDto; @@ -34,7 +43,9 @@ import com.c4soft.quiz.web.dto.QuizUpdateDto; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.headers.Header; +import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; @@ -47,180 +58,361 @@ @Validated @Tag(name = "Quizzes") public class QuizController { - private final QuizRepository quizRepo; - private final QuestionRepository questionRepo; - private final ChoiceRepository choiceRepo; - - @GetMapping - @Transactional(readOnly = true) - public List getQuizList() { - return quizRepo.findAll().stream().map(QuizController::toDto).toList(); - } - - @PostMapping - @PreAuthorize("hasAuthority('former')") - @Transactional(readOnly = false) - @Operation(responses = { - @ApiResponse(headers = @Header(name = HttpHeaders.LOCATION, description = "ID of the created quiz")) }) - public ResponseEntity createQuiz(@RequestBody @Valid QuizUpdateDto dto, Authentication auth) { - final var created = quizRepo.save(new Quiz(dto.title(), auth.getName())); - return ResponseEntity.created(URI.create("%d".formatted(created.getId()))).build(); - } - - @GetMapping("/{quiz-id}") - @Transactional(readOnly = true) - public QuizDto getQuiz(@PathVariable("quiz-id") Quiz quiz) { - return toDto(quiz); - } - - @PutMapping("/{quiz-id}") - @PreAuthorize("hasAuthority('moderator') || (hasAuthority('former') && #quiz.formerName == authentication.name)") - @Transactional(readOnly = false) - public ResponseEntity updateQuiz(@PathVariable("quiz-id") Quiz quiz, @RequestBody @Valid QuizUpdateDto dto) { - quiz.setTitle(dto.title()); - quizRepo.save(quiz); - return ResponseEntity.accepted().build(); - } - - @DeleteMapping("/{quiz-id}") - @PreAuthorize("hasAuthority('moderator') || (hasAuthority('former') && #quiz.formerName == authentication.name)") - @Transactional(readOnly = false) - public ResponseEntity deleteQuiz(@PathVariable("quiz-id") Quiz quiz) { - quizRepo.delete(quiz); - return ResponseEntity.accepted().build(); - } - - @PostMapping("/{quiz-id}/questions") - @PreAuthorize("hasAuthority('moderator') || (hasAuthority('former') && #quiz.formerName == authentication.name)") - @Transactional(readOnly = false) - @Operation(responses = { - @ApiResponse(headers = @Header(name = HttpHeaders.LOCATION, description = "ID of the created question")) }) - public ResponseEntity addQuestion(@PathVariable("quiz-id") Quiz quiz, - @RequestBody @Valid QuestionUpdateDto dto) { - final var question = new Question(dto.label(), quiz.getQuestions().size(), dto.comment()); - quiz.add(question); - final var created = questionRepo.save(question); - return ResponseEntity.created(URI.create("%d".formatted(created.getId()))).build(); - } - - @PutMapping("/{quiz-id}/questions") - @PreAuthorize("hasAuthority('moderator') || (hasAuthority('former') && #quiz.formerName == authentication.name)") - @Transactional(readOnly = false) - public ResponseEntity updateQuestionsOrder(@PathVariable("quiz-id") Quiz quiz, - @RequestBody @NotEmpty List questionIds) { - if (quiz.getQuestions().size() != questionIds.size()) { - return ResponseEntity.status(HttpStatus.NOT_ACCEPTABLE).build(); - } - final var quizQuestionIds = quiz.getQuestions().stream().map(Question::getId).collect(Collectors.toSet()); - for (var id : quizQuestionIds) { - if (!questionIds.contains(id)) { - return ResponseEntity.status(HttpStatus.NOT_ACCEPTABLE).build(); - } - } - quiz.getQuestions().stream().forEach(q -> q.setPriority(questionIds.indexOf(q.getId()))); - quiz = quizRepo.saveAndFlush(quiz); - return ResponseEntity.accepted().build(); - } - - @PutMapping("/{quiz-id}/questions/{question-id}") - @PreAuthorize("hasAuthority('moderator') || (hasAuthority('former') && #quiz.formerName == authentication.name)") - @Transactional(readOnly = false) - public ResponseEntity updateQuestion(@PathVariable("quiz-id") Quiz quiz, - @PathVariable("question-id") Long questionId, @RequestBody @Valid QuestionUpdateDto dto) { - final var question = quiz.getQuestion(questionId); - if(question == null) { - return ResponseEntity.notFound().build(); - } - question.setComment(dto.comment()); - question.setLabel(dto.label()); - questionRepo.save(question); - return ResponseEntity.accepted().build(); - } - - @DeleteMapping("/{quiz-id}/questions/{question-id}") - @PreAuthorize("hasAuthority('moderator') || (hasAuthority('former') && #quiz.formerName == authentication.name)") - @Transactional(readOnly = false) - public ResponseEntity deleteQuestion(@PathVariable("quiz-id") Quiz quiz, - @PathVariable("question-id") Long questionId) { - final var question = quiz.getQuestion(questionId); - if(question == null) { - return ResponseEntity.notFound().build(); - } - quiz.remove(question); - questionRepo.delete(question); - quizRepo.save(quiz); - return ResponseEntity.accepted().build(); - } - - @PostMapping("/{quiz-id}/questions/{question-id}/choices") - @PreAuthorize("hasAuthority('moderator') || (hasAuthority('former') && #quiz.formerName == authentication.name)") - @Transactional(readOnly = false) - public ResponseEntity addChoice(@PathVariable("quiz-id") Quiz quiz, - @PathVariable("question-id") Long questionId, @RequestBody @Valid ChoiceUpdateDto dto) { - final var question = quiz.getQuestion(questionId); - if(question == null) { - return ResponseEntity.notFound().build(); - } - final var choice = new Choice(dto.label(), dto.isGood()); - question.add(choice); - final var created = choiceRepo.save(choice); - return ResponseEntity.created(URI.create("%d".formatted(created.getId()))).build(); - } - - @PutMapping("/{quiz-id}/questions/{question-id}/choices/{choice-id}") - @PreAuthorize("hasAuthority('moderator') || (hasAuthority('former') && #quiz.formerName == authentication.name)") - @Transactional(readOnly = false) - public ResponseEntity updateChoice(@PathVariable("quiz-id") Quiz quiz, - @PathVariable("question-id") Long questionId, @PathVariable("choice-id") Long choiceId, - @RequestBody @Valid ChoiceUpdateDto dto) { - final var question = quiz.getQuestion(questionId); - if(question == null) { - return ResponseEntity.notFound().build(); - } - final var choice = question.getChoice(choiceId); - if(choice == null) { - return ResponseEntity.notFound().build(); - } - choice.setIsGood(dto.isGood()); - choice.setLabel(dto.label()); - choiceRepo.save(choice); - return ResponseEntity.accepted().build(); - } - - @DeleteMapping("/{quiz-id}/questions/{question-id}/choices/{choice-id}") - @PreAuthorize("hasAuthority('moderator') || (hasAuthority('former') && #quiz.formerName == authentication.name)") - @Transactional(readOnly = false) - public ResponseEntity deleteChoice(@PathVariable("quiz-id") Quiz quiz, - @PathVariable("question-id") Long questionId, @PathVariable("choice-id") Long choiceId) { - final var question = quiz.getQuestion(questionId); - if(question == null) { - return ResponseEntity.notFound().build(); - } - final var choice = question.getChoice(choiceId); - if(choice == null) { - return ResponseEntity.notFound().build(); - } - question.remove(choice); - choiceRepo.delete(choice); - questionRepo.save(question); - return ResponseEntity.accepted().build(); - } - - private static QuizDto toDto(Quiz q) { - return q == null ? null - : new QuizDto(q.getId(), q.getTitle(), q.getQuestions().stream() - .sorted((a, b) -> a.getPriority() - b.getPriority()).map(QuizController::toDto).toList()); - } - - private static QuestionDto toDto(Question q) { - return q == null ? null - : new QuestionDto(q.getQuiz().getId(), q.getId(), q.getLabel(), - q.getChoices().stream().map(QuizController::toDto).toList(), q.getComment()); - } - - private static ChoiceDto toDto(Choice c) { - return c == null ? null - : new ChoiceDto(c.getQuestion().getQuiz().getId(), c.getQuestion().getId(), c.getId(), c.getLabel(), - c.getIsGood()); - } + private final QuizRepository quizRepo; + private final QuestionRepository questionRepo; + private final ChoiceRepository choiceRepo; + private final SkillTestRepository skillTestRepo; + + @GetMapping(produces = MediaType.APPLICATION_JSON_VALUE) + @Transactional(readOnly = true) + public List getQuizList( + @RequestParam(name = "authorLike", required = false) Optional author, + @RequestParam(name = "titleLike", required = false) Optional title) { + final var spec = QuizRepository.searchSpec(author, title); + final var quizzes = quizRepo.findAll(spec); + return quizzes.stream().map(QuizController::toDto).toList(); + } + + @GetMapping(path = "/submitted", produces = MediaType.APPLICATION_JSON_VALUE) + @PreAuthorize("hasAuthority('moderator')") + @Transactional(readOnly = true) + public List getSubmittedQuizzes() { + final var quizzes = quizRepo.findByIsSubmitted(true); + return quizzes.stream().map(QuizController::toDto).toList(); + } + + @PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE) + @PreAuthorize("hasAuthority('trainer')") + @Transactional(readOnly = false) + @Operation(responses = { @ApiResponse(headers = @Header(name = HttpHeaders.LOCATION, description = "ID of the created quiz")) }) + public ResponseEntity createQuiz(@RequestBody @Valid QuizUpdateDto dto, QuizAuthentication auth) { + final var quiz = new Quiz(dto.title(), auth.getName()); + final var created = quizRepo.save(quiz); + return ResponseEntity.created(URI.create("%d".formatted(created.getId()))).build(); + } + + @GetMapping(path = "/{quiz-id}", produces = MediaType.APPLICATION_JSON_VALUE) + @Transactional(readOnly = true) + public QuizDto getQuiz(@Parameter(schema = @Schema(type = "integer")) @PathVariable("quiz-id") Quiz quiz) { + return toDto(quiz); + } + + @PutMapping(path = "/{quiz-id}", consumes = MediaType.APPLICATION_JSON_VALUE) + @PreAuthorize("hasAuthority('trainer') && #quiz.authorName == authentication.name") + @Transactional(readOnly = false) + public ResponseEntity updateQuiz( + @Parameter(schema = @Schema(type = "integer")) @PathVariable("quiz-id") Quiz quiz, + @RequestBody @Valid QuizUpdateDto dto, + QuizAuthentication auth) { + updateTitle(quiz, dto.title(), auth); + quiz.setIsChoicesShuffled(dto.isChoicesShuffled()); + quiz.setIsReplayEnabled(dto.isReplayEnabled()); + quiz.setIsPerQuestionResult(dto.isPerQuestionResult()); + quiz.setIsTrainerNotifiedOfNewTests(dto.isTrainerNotifiedOfNewTests()); + quizRepo.save(quiz); + return ResponseEntity.accepted().build(); + } + + void updateTitle(Quiz quiz, String title, QuizAuthentication auth) { + if (Objects.equals(quiz.getTitle(), title)) { + return; + } + if (quiz.getIsPublished() && !auth.isModerator()) { + throw new NotADraftException(quiz.getId()); + } + if (quiz.getReplacedBy() != null) { + throw new NotADraftException(quiz.getId()); + } + quiz.setTitle(title); + } + + @PostMapping(path = "/{quiz-id}/draft") + @PreAuthorize("hasAuthority('trainer') && #quiz.authorName == authentication.name") + @Transactional(readOnly = false) + public ResponseEntity createDraft(@Parameter(schema = @Schema(type = "integer")) @PathVariable("quiz-id") Quiz quiz, Authentication auth) { + if (quiz.getDraft() != null) { + throw new DraftAlreadyExistsException(quiz.getId()); + } + if (!quiz.getIsPublished()) { + throw new DraftAlreadyExistsException(quiz.getId()); + } + final var draft = new Quiz(quiz, auth.getName()); + final var created = quizRepo.save(draft); + quiz.setDraft(created); + quizRepo.save(quiz); + return ResponseEntity.created(URI.create("%d".formatted(created.getId()))).build(); + } + + @PutMapping(path = "/{quiz-id}/submit") + @PreAuthorize("hasAuthority('trainer') && #quiz.authorName == authentication.name") + @Transactional(readOnly = false) + public ResponseEntity submitDraft(@Parameter(schema = @Schema(type = "integer")) @PathVariable("quiz-id") Quiz quiz, QuizAuthentication auth) { + if (auth.isModerator()) { + return publishDraft(quiz, auth); + } + quiz.setIsSubmitted(!auth.isModerator()); + quizRepo.save(quiz); + return ResponseEntity.accepted().build(); + } + + @PutMapping(path = "/{quiz-id}/publish") + @PreAuthorize("hasAuthority('moderator')") + @Transactional(readOnly = false) + public ResponseEntity publishDraft(@Parameter(schema = @Schema(type = "integer")) @PathVariable("quiz-id") Quiz quiz, Authentication auth) { + quiz.setIsSubmitted(false); + quiz.setIsPublished(true); + quiz.setModeratorComment(null); + quiz.setModeratedBy(auth.getName()); + if (quiz.getReplaces() != null) { + final var replaced = quiz.getReplaces(); + replaced.setReplacedBy(quiz); + replaced.setIsPublished(false); + quiz.setReplaces(null); + quizRepo.save(replaced); + } + + quizRepo.save(quiz); + return ResponseEntity.accepted().build(); + } + + @PutMapping(path = "/{quiz-id}/reject", consumes = MediaType.APPLICATION_JSON_VALUE) + @PreAuthorize("hasAuthority('moderator')") + @Transactional(readOnly = false) + public + ResponseEntity + rejectDraft(@Parameter(schema = @Schema(type = "integer")) @PathVariable("quiz-id") Quiz quiz, @Valid QuizRejectionDto dto, Authentication auth) { + quiz.setIsSubmitted(false); + quiz.setIsPublished(false); + quiz.setModeratorComment(dto.message()); + quiz.setModeratedBy(auth.getName()); + quizRepo.save(quiz); + return ResponseEntity.accepted().build(); + } + + @DeleteMapping(path = "/{quiz-id}") + @PreAuthorize("hasAuthority('moderator') || (hasAuthority('trainer') && #quiz.authorName == authentication.name)") + @Transactional(readOnly = false) + public ResponseEntity deleteQuiz(@Parameter(schema = @Schema(type = "integer")) @PathVariable("quiz-id") Quiz quiz) { + final var draft = quiz.getDraft(); + if (draft != null) { + draft.setReplaces(null); + quiz.setDraft(null); + quizRepo.save(draft); + } + + final var replacedBy = quiz.getReplacedBy(); + if (replacedBy != null) { + replacedBy.setReplaces(null); + quiz.setReplacedBy(null); + quizRepo.save(replacedBy); + } + + final var replaces = quiz.getReplaces(); + if (replaces != null) { + if (replaces.getReplacedBy() != null && Objects.equals(replaces.getReplacedBy().getId(), quiz.getId())) { + replaces.setReplacedBy(null); + } + if (replaces.getDraft().getId() != null && Objects.equals(replaces.getDraft().getId(), quiz.getId())) { + replaces.setDraft(null); + } + quiz.setReplaces(null); + quizRepo.save(replaces); + } + + skillTestRepo.deleteByIdQuizId(quiz.getId()); + questionRepo.deleteAll(quiz.getQuestions()); + quiz.getQuestions().clear(); + + quizRepo.delete(quiz); + return ResponseEntity.accepted().build(); + } + + @PostMapping(path = "/{quiz-id}/questions", consumes = MediaType.APPLICATION_JSON_VALUE) + @PreAuthorize("hasAuthority('trainer') && #quiz.authorName == authentication.name") + @Transactional(readOnly = false) + @Operation(responses = { @ApiResponse(headers = @Header(name = HttpHeaders.LOCATION, description = "ID of the created question")) }) + public + ResponseEntity + addQuestion(@Parameter(schema = @Schema(type = "integer")) @PathVariable("quiz-id") Quiz quiz, @RequestBody @Valid QuestionUpdateDto dto) { + if (quiz.getIsPublished() || quiz.getReplacedBy() != null) { + throw new NotADraftException(quiz.getId()); + } + final var question = new Question(dto.label(), quiz.getQuestions().size(), dto.comment()); + quiz.add(question); + final var created = questionRepo.save(question); + return ResponseEntity.created(URI.create("%d".formatted(created.getId()))).build(); + } + + @PutMapping(path = "/{quiz-id}/questions", consumes = MediaType.APPLICATION_JSON_VALUE) + @PreAuthorize("hasAuthority('trainer') && #quiz.authorName == authentication.name") + @Transactional(readOnly = false) + public ResponseEntity updateQuestionsOrder( + @Parameter(schema = @Schema(type = "integer")) @PathVariable("quiz-id") Quiz quiz, + @RequestBody @NotEmpty List questionIds) { + if (quiz.getQuestions().size() != questionIds.size()) { + return ResponseEntity.status(HttpStatus.NOT_ACCEPTABLE).build(); + } + final var quizQuestionIds = quiz.getQuestions().stream().map(Question::getId).collect(Collectors.toSet()); + for (var id : quizQuestionIds) { + if (!questionIds.contains(id)) { + return ResponseEntity.status(HttpStatus.NOT_ACCEPTABLE).build(); + } + } + quiz.getQuestions().stream().forEach(q -> q.setPriority(questionIds.indexOf(q.getId()))); + quiz = quizRepo.saveAndFlush(quiz); + return ResponseEntity.accepted().build(); + } + + @PutMapping(path = "/{quiz-id}/questions/{question-id}", consumes = MediaType.APPLICATION_JSON_VALUE) + @PreAuthorize("hasAuthority('trainer') && #quiz.authorName == authentication.name && !#quiz.isPublished") + @Transactional(readOnly = false) + public ResponseEntity updateQuestion( + @Parameter(schema = @Schema(type = "integer")) @PathVariable("quiz-id") Quiz quiz, + @Parameter(schema = @Schema(type = "integer")) @PathVariable("question-id") Long questionId, + @RequestBody @Valid QuestionUpdateDto dto) { + if (quiz.getIsPublished() || quiz.getReplacedBy() != null) { + throw new NotADraftException(quiz.getId()); + } + final var question = quiz.getQuestion(questionId); + if (question == null) { + return ResponseEntity.notFound().build(); + } + question.setComment(dto.comment()); + question.setLabel(dto.label()); + questionRepo.save(question); + return ResponseEntity.accepted().build(); + } + + @DeleteMapping(path = "/{quiz-id}/questions/{question-id}") + @PreAuthorize("hasAuthority('trainer') && #quiz.authorName == authentication.name") + @Transactional(readOnly = false) + public ResponseEntity deleteQuestion( + @Parameter(schema = @Schema(type = "integer")) @PathVariable("quiz-id") Quiz quiz, + @Parameter(schema = @Schema(type = "integer")) @PathVariable("question-id") Long questionId) { + final var question = quiz.getQuestion(questionId); + if (question == null) { + return ResponseEntity.notFound().build(); + } + + final var tests = skillTestRepo.findByIdQuizId(quiz.getId()); + tests.forEach(t -> { + final var filtered = t.getChoices().stream().filter(c -> { + return c.getQuestion().getId() != questionId; + }).toList(); + t.setChoices(filtered); + }); + skillTestRepo.saveAll(tests); + + quiz.remove(question); + quizRepo.save(quiz); + choiceRepo.deleteAll(question.getChoices()); + questionRepo.delete(question); + return ResponseEntity.accepted().build(); + } + + @PostMapping(path = "/{quiz-id}/questions/{question-id}/choices", consumes = MediaType.APPLICATION_JSON_VALUE) + @PreAuthorize("hasAuthority('trainer') && #quiz.authorName == authentication.name && !#quiz.isPublished") + @Transactional(readOnly = false) + public ResponseEntity addChoice( + @Parameter(schema = @Schema(type = "integer")) @PathVariable("quiz-id") Quiz quiz, + @Parameter(schema = @Schema(type = "integer")) @PathVariable("question-id") Long questionId, + @RequestBody @Valid ChoiceUpdateDto dto) { + if (quiz.getIsPublished() || quiz.getReplacedBy() != null) { + throw new NotADraftException(quiz.getId()); + } + final var question = quiz.getQuestion(questionId); + if (question == null) { + return ResponseEntity.notFound().build(); + } + final var choice = new Choice(dto.label(), dto.isGood()); + question.add(choice); + final var created = choiceRepo.save(choice); + return ResponseEntity.created(URI.create("%d".formatted(created.getId()))).build(); + } + + @PutMapping(path = "/{quiz-id}/questions/{question-id}/choices/{choice-id}", consumes = MediaType.APPLICATION_JSON_VALUE) + @PreAuthorize("hasAuthority('trainer') && #quiz.authorName == authentication.name") + @Transactional(readOnly = false) + public ResponseEntity updateChoice( + @Parameter(schema = @Schema(type = "integer")) @PathVariable("quiz-id") Quiz quiz, + @Parameter(schema = @Schema(type = "integer")) @PathVariable("question-id") Long questionId, + @Parameter(schema = @Schema(type = "integer")) @PathVariable("choice-id") Long choiceId, + @RequestBody @Valid ChoiceUpdateDto dto) { + final var question = quiz.getQuestion(questionId); + if (question == null) { + return ResponseEntity.notFound().build(); + } + final var choice = question.getChoice(choiceId); + if (choice == null) { + return ResponseEntity.notFound().build(); + } + if (!Objects.equals(dto.label(), choice.getLabel()) && (quiz.getIsPublished() || quiz.getReplacedBy() != null)) { + throw new NotADraftException(quiz.getId()); + } + choice.setIsGood(dto.isGood()); + choice.setLabel(dto.label()); + choiceRepo.save(choice); + return ResponseEntity.accepted().build(); + } + + @DeleteMapping(path = "/{quiz-id}/questions/{question-id}/choices/{choice-id}") + @PreAuthorize("hasAuthority('trainer') && #quiz.authorName == authentication.name") + @Transactional(readOnly = false) + public ResponseEntity deleteChoice( + @Parameter(schema = @Schema(type = "integer")) @PathVariable("quiz-id") Quiz quiz, + @Parameter(schema = @Schema(type = "integer")) @PathVariable("question-id") Long questionId, + @Parameter(schema = @Schema(type = "integer")) @PathVariable("choice-id") Long choiceId) { + final var question = quiz.getQuestion(questionId); + if (question == null) { + return ResponseEntity.notFound().build(); + } + final var choice = question.getChoice(choiceId); + if (choice == null) { + return ResponseEntity.notFound().build(); + } + + final var tests = skillTestRepo.findByIdQuizId(quiz.getId()); + tests.forEach(t -> { + final var filtered = t.getChoices().stream().filter(c -> { + return c.getId() != choiceId; + }).toList(); + t.setChoices(filtered); + }); + skillTestRepo.saveAll(tests); + + question.remove(choice); + choiceRepo.delete(choice); + questionRepo.save(question); + return ResponseEntity.accepted().build(); + } + + private static QuizDto toDto(Quiz q) { + return q == null + ? null + : new QuizDto( + q.getId(), + q.getTitle(), + q.getQuestions().stream().sorted((a, b) -> a.getPriority() - b.getPriority()).map(QuizController::toDto).toList(), + q.getAuthorName(), + q.getIsPublished(), + q.getIsSubmitted(), + q.getReplacedBy() != null, + q.getIsChoicesShuffled(), + q.getIsReplayEnabled(), + q.getIsPerQuestionResult(), + q.getIsTrainerNotifiedOfNewTests(), + q.getModeratorComment(), + q.getDraft() == null ? null : q.getDraft().getId(), + q.getReplaces() == null ? null : q.getReplaces().getId()); + } + + private static QuestionDto toDto(Question q) { + return q == null + ? null + : new QuestionDto(q.getQuiz().getId(), q.getId(), q.getLabel(), q.getChoices().stream().map(QuizController::toDto).toList(), q.getComment()); + } + + private static ChoiceDto toDto(Choice c) { + return c == null ? null : new ChoiceDto(c.getQuestion().getQuiz().getId(), c.getQuestion().getId(), c.getId(), c.getLabel(), c.getIsGood()); + } } diff --git a/api/quiz-api/src/main/java/com/c4soft/quiz/web/SkillTestController.java b/api/quiz-api/src/main/java/com/c4soft/quiz/web/SkillTestController.java index e20c536..56e5a16 100644 --- a/api/quiz-api/src/main/java/com/c4soft/quiz/web/SkillTestController.java +++ b/api/quiz-api/src/main/java/com/c4soft/quiz/web/SkillTestController.java @@ -1,32 +1,48 @@ package com.c4soft.quiz.web; +import java.net.URI; +import java.net.URISyntaxException; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.List; import java.util.Optional; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; +import org.springframework.mail.SimpleMailMessage; +import org.springframework.mail.javamail.JavaMailSender; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.Authentication; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestController; import com.c4soft.quiz.domain.Choice; +import com.c4soft.quiz.domain.Quiz; import com.c4soft.quiz.domain.QuizRepository; import com.c4soft.quiz.domain.SkillTest; +import com.c4soft.quiz.domain.SkillTest.SkillTestPk; import com.c4soft.quiz.domain.SkillTestRepository; +import com.c4soft.quiz.feign.KeycloakAdminApiClient; import com.c4soft.quiz.web.dto.SkillTestDto; import com.c4soft.quiz.web.dto.SkillTestQuestionDto; -import com.c4soft.quiz.web.dto.SkillTestResultDto; +import com.c4soft.quiz.web.dto.SkillTestResultDetailsDto; +import com.c4soft.quiz.web.dto.SkillTestResultPreviewDto; import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.persistence.EntityNotFoundException; import jakarta.validation.Valid; @@ -40,29 +56,47 @@ public class SkillTestController { private final SkillTestRepository testRepo; private final QuizRepository quizRepo; + private final KeycloakAdminApiClient keycloakAdminApi; + private final JavaMailSender mailSender; + @Value("${ui-uri}") + URI uiUri; - @GetMapping + @GetMapping(path = "/{quizId}", produces = MediaType.APPLICATION_JSON_VALUE) @Transactional(readOnly = true) @Operation(description = "Returns the answers to a quiz, by default for all trainees over the last 2 weeks") - public List getTestList( - @RequestParam(value = "quizId", required = true) Long quizId, + public List getSkillTestList( + @PathVariable(value = "quizId", required = true) Long quizId, @RequestParam(value = "since", required = false) Optional since, - @RequestParam(value = "until", required = false) Optional until, - @RequestParam(value = "traineeName", required = false) Optional traineeName) { - final var resolvedSince = since.orElse(Instant.now().minus(14, ChronoUnit.DAYS).toEpochMilli()); - final var tests = traineeName.isEmpty() - ? testRepo.findByIdQuizId(quizId).stream() - : testRepo.findAll(SkillTestRepository.spec(quizId, resolvedSince, until, traineeName)) - .stream(); - return tests.map(this::toDto).toList(); + @RequestParam(value = "until", required = false) Optional until) { + final var resolvedSince = since.map(sec -> 1000 * sec).orElse(Instant.now().minus(14, ChronoUnit.DAYS).toEpochMilli()); + final var tests = testRepo.findAll(SkillTestRepository.spec(quizId, resolvedSince, until.map(sec -> 1000 * sec))).stream(); + return tests.map(this::toPreviewDto).toList(); } - @PutMapping + @GetMapping(path = "/{quizId}/{traineeName}", produces = MediaType.APPLICATION_JSON_VALUE) + @Transactional(readOnly = true) + @Operation(description = "Returns the answers to a quiz, by default for all trainees over the last 2 weeks") + public SkillTestResultDetailsDto getSkillTest( + @PathVariable(value = "quizId", required = true) Long quizId, + @PathVariable(value = "traineeName", required = true) String traineeName) { + final var skillTest = testRepo.findById(new SkillTestPk(quizId, traineeName)); + if (skillTest.isEmpty()) { + throw new EntityNotFoundException("No skill-test for quiz %d and trainee %s".formatted(quizId, traineeName)); + } + return toDetailsDto(skillTest.get()); + } + + @PutMapping(consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) @PreAuthorize("isAuthenticated()") @Transactional(readOnly = false) - public ResponseEntity submitQuizChoices(@RequestBody @Valid SkillTestDto dto, Authentication auth) { - final var quiz = - quizRepo.findById(dto.quizId()).orElseThrow(() -> new EntityNotFoundException("Quiz %d was removed from the database".formatted(dto.quizId()))); + public ResponseEntity submitSkillTest(@RequestBody @Valid SkillTestDto dto, Authentication auth) { + final var quiz = quizRepo.findById(dto.quizId()).orElseThrow(() -> new InvalidQuizException(dto.quizId())); + if (!quiz.getIsPublished()) { + throw new InvalidQuizException(dto.quizId()); + } + if (!quiz.getIsReplayEnabled() && testRepo.findByIdQuizIdAndIdTraineeName(dto.quizId(), auth.getName()).isPresent()) { + throw new QuizAlreadyHasAnAnswerException(dto.quizId(), auth.getName()); + } final var traineeChoices = new ArrayList(); for (var question : quiz.getQuestions()) { final var questionDto = dto.getQuestion(question.getId()); @@ -72,14 +106,37 @@ public ResponseEntity submitQuizChoices(@RequestBody @Valid SkillTestDto d } } } - final var test = testRepo.findByIdQuizIdAndIdTraineeName(quiz.getId(), auth.getName()).orElse(new SkillTest(auth.getName(), traineeChoices)); + final var test = testRepo.findByIdQuizIdAndIdTraineeName(quiz.getId(), auth.getName()).orElse(new SkillTest(quiz, auth.getName(), traineeChoices)); test.setSubmittedOn(Instant.now().toEpochMilli()); test.setChoices(traineeChoices); - testRepo.save(test); + final var saved = testRepo.save(test); + if (quiz.getIsTrainerNotifiedOfNewTests()) { + final var users = keycloakAdminApi.getUser(quiz.getAuthorName(), true); + + if (users.size() == 1) { + SimpleMailMessage message = new SimpleMailMessage(); + message.setFrom("noreply@c4-soft.com"); + message.setTo(users.get(0).getEmail()); + message.setSubject("C4 - Quiz: New answer to %s by %s".formatted(quiz.getTitle(), auth.getName())); + message.setText("%s/tests/%d/%s".formatted(uiUri, quiz.getId(), auth.getName())); + mailSender.send(message); + } + } + return ResponseEntity.ok(toPreviewDto(saved)); + } + + @DeleteMapping(path = "/{quizId}/{traineeName}") + @PreAuthorize("authentication.name == #quiz.authorName") + @Transactional(readOnly = false) + @Operation(description = "Deletes the answer to given quiz for given trainee. Only the author of a quiz can delete skill-tests.") + public ResponseEntity deleteSkillTest( + @Parameter(schema = @Schema(type = "integer")) @PathVariable(value = "quizId", required = true) Quiz quiz, + @PathVariable(value = "traineeName", required = true) String traineeName) { + testRepo.deleteById(new SkillTestPk(quiz.getId(), traineeName)); return ResponseEntity.accepted().build(); } - private SkillTestResultDto toDto(SkillTest traineeAnswer) { + private SkillTestResultPreviewDto toPreviewDto(SkillTest traineeAnswer) { var score = 0; var totalChoices = 0; final var quiz = quizRepo.findById(traineeAnswer.getId().getQuizId()) @@ -99,6 +156,62 @@ private SkillTestResultDto toDto(SkillTest traineeAnswer) { } } } - return new SkillTestResultDto(testDto, totalChoices == 0 ? null : 100.0 * score / totalChoices); + + return new SkillTestResultPreviewDto(traineeAnswer.getId().getTraineeName(), totalChoices == 0 ? null : 100.0 * score / totalChoices); + } + + private SkillTestResultDetailsDto toDetailsDto(SkillTest traineeAnswer) { + var score = 0; + var totalChoices = 0; + final var quiz = quizRepo.findById(traineeAnswer.getId().getQuizId()) + .orElseThrow(() -> new EntityNotFoundException("Unknown quiz: %d".formatted(traineeAnswer.getId().getQuizId()))); + final var testDto = new SkillTestDto(traineeAnswer.getId().getQuizId(), new ArrayList<>()); + for (var question : quiz.getQuestions()) { + final var traineeQuestionChoices = traineeAnswer.getChoices(question.getId()); + final var questionDto = new SkillTestQuestionDto(question.getId(), new ArrayList<>()); + testDto.questions().add(questionDto); + for (var choice : question.getChoices()) { + totalChoices += 1; + if (traineeQuestionChoices.contains(choice)) { + questionDto.choices().add(choice.getId()); + score += choice.getIsGood() ? 1 : -1; + } else { + score += choice.getIsGood() ? 0 : 1; + } + } + } + + final var users = keycloakAdminApi.getUser(traineeAnswer.getId().getTraineeName(), true); + final var email = users.size() == 1 ? users.get(0).getEmail() : ""; + final var firstName = users.size() == 1 ? users.get(0).getFirtsName() : ""; + final var lastName = users.size() == 1 ? users.get(0).getLastName() : ""; + return new SkillTestResultDetailsDto( + testDto, + traineeAnswer.getId().getTraineeName(), + firstName, + lastName, + email, + totalChoices == 0 ? null : 100.0 * score / totalChoices); + } + + @ResponseStatus(HttpStatus.CONFLICT) + static class InvalidQuizException extends RuntimeException { + private static final long serialVersionUID = 8816930385638385805L; + + InvalidQuizException(Long quizId) { + super("Quiz %d doesn't accept answers anymore.".formatted(quizId)); + } + } + + @ResponseStatus(HttpStatus.CONFLICT) + static class QuizAlreadyHasAnAnswerException extends RuntimeException { + + private static final long serialVersionUID = 6171083302507116601L; + + QuizAlreadyHasAnAnswerException(Long quizId, String traineeName) { + super( + "Quiz %d already has an answer for %s and doesn't accept replay. Ask the trainer to delete the answer before submitting a new one." + .formatted(quizId, traineeName)); + } } } diff --git a/api/quiz-api/src/main/java/com/c4soft/quiz/web/UsersController.java b/api/quiz-api/src/main/java/com/c4soft/quiz/web/UsersController.java new file mode 100644 index 0000000..d6fedc0 --- /dev/null +++ b/api/quiz-api/src/main/java/com/c4soft/quiz/web/UsersController.java @@ -0,0 +1,34 @@ +package com.c4soft.quiz.web; + +import org.springframework.http.MediaType; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.c4soft.quiz.domain.QuizAuthentication; +import com.c4soft.quiz.web.dto.UserInfoDto; + +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; + +@RestController +@RequestMapping(path = "/users") +@RequiredArgsConstructor +@Validated +@Tag(name = "Users") +public class UsersController { + + @GetMapping(path = "/me", produces = MediaType.APPLICATION_JSON_VALUE) + public UserInfoDto getMe(Authentication auth) { + if (auth instanceof QuizAuthentication quizAuth) { + return new UserInfoDto( + quizAuth.getName(), + quizAuth.getAuthorities().stream().map(GrantedAuthority::getAuthority).toList(), + quizAuth.getAttributes().getExpiresAt().getEpochSecond()); + } + return UserInfoDto.ANONYMOUS; + } +} diff --git a/api/quiz-api/src/main/java/com/c4soft/quiz/web/dto/QuestionUpdateDto.java b/api/quiz-api/src/main/java/com/c4soft/quiz/web/dto/QuestionUpdateDto.java index fceee9d..6395618 100644 --- a/api/quiz-api/src/main/java/com/c4soft/quiz/web/dto/QuestionUpdateDto.java +++ b/api/quiz-api/src/main/java/com/c4soft/quiz/web/dto/QuestionUpdateDto.java @@ -3,5 +3,9 @@ import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; +/** + * @parameter label the new label for the question + * @parameter comment a new explanation for the right answer + */ public record QuestionUpdateDto(@NotEmpty String label, @NotNull String comment) { } \ No newline at end of file diff --git a/api/quiz-api/src/main/java/com/c4soft/quiz/web/dto/QuizDto.java b/api/quiz-api/src/main/java/com/c4soft/quiz/web/dto/QuizDto.java index 9535e61..f2791e1 100644 --- a/api/quiz-api/src/main/java/com/c4soft/quiz/web/dto/QuizDto.java +++ b/api/quiz-api/src/main/java/com/c4soft/quiz/web/dto/QuizDto.java @@ -5,5 +5,36 @@ import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; -public record QuizDto(@NotNull Long id, @NotEmpty String title, @NotNull List questions) { +/** + * @param id unique identifier for the quiz + * @param title the quiz title + * @param questions an array with all of the quiz questions + * @param authorName name of the trainer who authored the quiz + * @param isPublished is the quiz available for trainees + * @param isSubmitted is the quiz submitted to moderation + * @param isReplaced was a new version of this quiz published (and replaces this one) + * @param isChoicesShuffled should the choices display order be shuffled from a question display to another + * @param isReplayEnabled can a trainee submit a new answer before his former one was deleted by the trainer + * @param isPerQuestionResult should the right answer as well as comment be displayed as soon as choices are validated for a question or only when the + * skill test was accepted by the server + * @param isTrainerNotifiedOfNewTests if true, trainers receive an email each time a new skill-test is submitted (for quizzes they authored only) + * @param ModeratorComment an explanation why this quiz version was rejected by a moderator + * @param draftId unique identifier for a modified version of this quiz + * @param replacesId identifier of the former version of this quiz + */ +public record QuizDto( + @NotNull Long id, + @NotEmpty String title, + @NotNull List questions, + @NotEmpty String authorName, + @NotNull Boolean isPublished, + @NotNull Boolean isSubmitted, + @NotNull Boolean isReplaced, + @NotNull Boolean isChoicesShuffled, + @NotNull Boolean isReplayEnabled, + @NotNull Boolean isPerQuestionResult, + @NotNull Boolean isTrainerNotifiedOfNewTests, + String ModeratorComment, + Long draftId, + Long replacesId) { } \ No newline at end of file diff --git a/api/quiz-api/src/main/java/com/c4soft/quiz/web/dto/QuizUpdateDto.java b/api/quiz-api/src/main/java/com/c4soft/quiz/web/dto/QuizUpdateDto.java index 52fefae..470211f 100644 --- a/api/quiz-api/src/main/java/com/c4soft/quiz/web/dto/QuizUpdateDto.java +++ b/api/quiz-api/src/main/java/com/c4soft/quiz/web/dto/QuizUpdateDto.java @@ -1,6 +1,20 @@ package com.c4soft.quiz.web.dto; import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; -public record QuizUpdateDto(@NotEmpty String title) { +/** + * @param title the new title for the quiz + * @param isChoicesShuffled should choices display order be randomized from a display to another. + * @param isReplayEnabled can a trainee submit a new skill test before the former one was deleted by the trainer. + * @param isPerQuestionResult if true, the right answer as well as comment should be displayed as soon as choices for a question are validated. + * Otherwise, it should be displayed only when the test was accepted by the server. + * @param isTrainerNotifiedOfNewTests if true, trainers receive an email each time a new skill-test is submitted (for quizzes they authored only) + */ +public record QuizUpdateDto( + @NotEmpty String title, + @NotNull Boolean isChoicesShuffled, + @NotNull Boolean isReplayEnabled, + @NotNull Boolean isPerQuestionResult, + @NotNull Boolean isTrainerNotifiedOfNewTests) { } \ No newline at end of file diff --git a/api/quiz-api/src/main/java/com/c4soft/quiz/web/dto/SkillTestDto.java b/api/quiz-api/src/main/java/com/c4soft/quiz/web/dto/SkillTestDto.java index a3c3ff6..bfdd3f0 100644 --- a/api/quiz-api/src/main/java/com/c4soft/quiz/web/dto/SkillTestDto.java +++ b/api/quiz-api/src/main/java/com/c4soft/quiz/web/dto/SkillTestDto.java @@ -1,12 +1,13 @@ package com.c4soft.quiz.web.dto; import java.util.List; +import java.util.Objects; import jakarta.validation.constraints.NotEmpty; import jakarta.validation.constraints.NotNull; public record SkillTestDto(@NotNull Long quizId, @NotEmpty List questions) { - public SkillTestQuestionDto getQuestion(Long questionId) { - return questions.stream().filter(q -> q.questionId() == questionId).findAny().orElse(new SkillTestQuestionDto(questionId, List.of())); - } + public SkillTestQuestionDto getQuestion(Long questionId) { + return questions.stream().filter(q -> Objects.equals(q.questionId(), questionId)).findAny().orElse(new SkillTestQuestionDto(questionId, List.of())); + } } \ No newline at end of file diff --git a/api/quiz-api/src/main/java/com/c4soft/quiz/web/dto/SkillTestResultDetailsDto.java b/api/quiz-api/src/main/java/com/c4soft/quiz/web/dto/SkillTestResultDetailsDto.java new file mode 100644 index 0000000..afbcf8e --- /dev/null +++ b/api/quiz-api/src/main/java/com/c4soft/quiz/web/dto/SkillTestResultDetailsDto.java @@ -0,0 +1,12 @@ +package com.c4soft.quiz.web.dto; + +import jakarta.validation.constraints.NotNull; + +public record SkillTestResultDetailsDto( + @NotNull SkillTestDto test, + @NotNull String traineeUsername, + @NotNull String traineeFirstName, + @NotNull String traineeLastName, + @NotNull String traineeEmail, + @NotNull Double score) { +} \ No newline at end of file diff --git a/api/quiz-api/src/main/java/com/c4soft/quiz/web/dto/SkillTestResultDto.java b/api/quiz-api/src/main/java/com/c4soft/quiz/web/dto/SkillTestResultDto.java deleted file mode 100644 index 730b0a1..0000000 --- a/api/quiz-api/src/main/java/com/c4soft/quiz/web/dto/SkillTestResultDto.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.c4soft.quiz.web.dto; - -import jakarta.validation.constraints.NotNull; - -public record SkillTestResultDto(@NotNull SkillTestDto test, @NotNull Double score) {} \ No newline at end of file diff --git a/api/quiz-api/src/main/java/com/c4soft/quiz/web/dto/SkillTestResultPreviewDto.java b/api/quiz-api/src/main/java/com/c4soft/quiz/web/dto/SkillTestResultPreviewDto.java new file mode 100644 index 0000000..5bbc194 --- /dev/null +++ b/api/quiz-api/src/main/java/com/c4soft/quiz/web/dto/SkillTestResultPreviewDto.java @@ -0,0 +1,5 @@ +package com.c4soft.quiz.web.dto; + +import jakarta.validation.constraints.NotNull; + +public record SkillTestResultPreviewDto(@NotNull String traineeName, @NotNull Double score) {} \ No newline at end of file diff --git a/api/quiz-api/src/main/java/com/c4soft/quiz/web/dto/UserInfoDto.java b/api/quiz-api/src/main/java/com/c4soft/quiz/web/dto/UserInfoDto.java new file mode 100644 index 0000000..4a74faa --- /dev/null +++ b/api/quiz-api/src/main/java/com/c4soft/quiz/web/dto/UserInfoDto.java @@ -0,0 +1,14 @@ +package com.c4soft.quiz.web.dto; + +import java.util.List; + +import jakarta.validation.constraints.NotNull; + +/** + * @param username the user unique name + * @param roles the user roles + * @param exp seconds since epoch time at which access will expire + */ +public record UserInfoDto(@NotNull String username, @NotNull List roles, @NotNull Long exp) { + public static final UserInfoDto ANONYMOUS = new UserInfoDto("", List.of(), Long.MAX_VALUE); +} \ No newline at end of file diff --git a/api/quiz-api/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/api/quiz-api/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 9cf2b63..e48b6fe 100644 --- a/api/quiz-api/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/api/quiz-api/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -8,5 +8,15 @@ "name": "oauth2-client-id", "type": "java.lang.String", "description": "client-id used for resource_access roles" + }, + { + "name": "keycloak-host", + "type": "java.lang.String", + "description": "Scheme, hostname and port for Keycloak server" + }, + { + "name": "keycloak-realm", + "type": "java.lang.String", + "description": "Keycloak 'realm'" } ]} \ No newline at end of file diff --git a/api/quiz-api/src/main/resources/application.yml b/api/quiz-api/src/main/resources/application.yml index ad82cdf..a062a4d 100644 --- a/api/quiz-api/src/main/resources/application.yml +++ b/api/quiz-api/src/main/resources/application.yml @@ -1,5 +1,7 @@ -oauth2-issuer: https://oidc.c4-soft.com/auth/realms/master -oauth2-client-id: quiz +keycloak-host: https://oidc.c4-soft.com +keycloak-realm: quiz +oauth2-issuer: ${keycloak-host}/auth/realms/${keycloak-realm} +ui-uri: https://localhost:8080/ui server: port: 7084 @@ -25,6 +27,42 @@ spring: generate-ddl: true hibernate: ddl-auto: update + show-sql: false + cloud: + openfeign: + client: + config: + keycloak-admin-api: + url: ${keycloak-host}/auth/admin/realms/${keycloak-realm} + oauth2: + enabled: true + clientRegistrationId: quiz-admin + mail: + host: smtp.gmail.com + port: 587 + username: ch4mp@c4-soft.com + password: change-me + properties: + mail: + smtp: + auth: true + starttls: + enable: true + security: + oauth2: + client: + provider: + keycloak: + issuer-uri: ${oauth2-issuer} + registration: + quiz-admin: + provider: keycloak + client-id: quiz-admin + client-secret: change-me + authorization-grant-type: client_credentials + scope: + - openid + - offline_access com: c4-soft: @@ -35,9 +73,9 @@ com: username-claim: preferred_username authorities: - path: $.realm_access.roles - - path: $.resource_access.${oauth2-client-id}.roles resourceserver: permit-all: + - "/users/me" - "/quizzes/**" - "/actuator/health/readiness" - "/actuator/health/liveness" @@ -62,7 +100,7 @@ logging: level: org: springframework: - security: DEBUG + security: INFO --- spring: diff --git a/api/quiz-api/src/main/resources/banner.txt b/api/quiz-api/src/main/resources/banner.txt index e69e98c..d952896 100644 --- a/api/quiz-api/src/main/resources/banner.txt +++ b/api/quiz-api/src/main/resources/banner.txt @@ -4,17 +4,11 @@ | | |__ _||______| \___ \ / _ \ | _|| __| / __| / _ \ | '_ ` _ \ | |____ | | ____) || (_) || | | |_ _ | (__ | (_) || | | | | | \_____| |_| |_____/ \___/ |_| \__|(_) \___| \___/ |_| |_| |_| - ____ _____ _____ _______ _ _ - / __ \ |_ _|| __ \ |__ __| (_) (_) -| | | | _ __ ___ _ __ | | | | | | | | _ __ __ _ _ _ __ _ _ __ __ _ -| | | || '_ \ / _ \| '_ \ | | | | | | | | | '__| / _` || || '_ \ | || '_ \ / _` | -| |__| || |_) || __/| | | | _| |_ | |__| | | | | | | (_| || || | | || || | | || (_| | - \____/ | .__/ \___||_| |_||_____||_____/ |_| |_| \__,_||_||_| |_||_||_| |_| \__, | - | | __/ | - |_| |___/ - _ _ _____ _____ -| | | | /\ | __ \ |_ _| -| | | | ___ ___ _ __ ___ / \ | |__) | | | -| | | |/ __| / _ \| '__|/ __| / /\ \ | ___/ | | -| |__| |\__ \| __/| | \__ \ / ____ \ | | _| |_ - \____/ |___/ \___||_| |___/ /_/ \_\|_| |_____| \ No newline at end of file + ____ _ _____ ______ _____ _______ _____ _____ + / __ \ (_) | __ \| ____|/ ____|__ __| /\ | __ \_ _| + | | | |_ _ _ ____ | |__) | |__ | (___ | | / \ | |__) || | + | | | | | | | |_ / | _ /| __| \___ \ | | / /\ \ | ___/ | | + | |__| | |_| | |/ / | | \ \| |____ ____) | | | / ____ \| | _| |_ + \___\_\\__,_|_/___| |_| \_\______|_____/ |_| /_/ \_\_| |_____| + + \ No newline at end of file diff --git a/api/quiz-api/src/test/java/com/c4soft/quiz/QuizApiApplicationTest.java b/api/quiz-api/src/test/java/com/c4soft/quiz/QuizApiApplicationTest.java index 046cae6..8f60fad 100644 --- a/api/quiz-api/src/test/java/com/c4soft/quiz/QuizApiApplicationTest.java +++ b/api/quiz-api/src/test/java/com/c4soft/quiz/QuizApiApplicationTest.java @@ -4,6 +4,7 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.collection.IsCollectionWithSize.hasSize; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -11,12 +12,14 @@ import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.List; +import java.util.Map; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.mock.mockito.MockBean; import org.springframework.context.annotation.Import; import org.springframework.http.HttpHeaders; import org.springframework.test.context.ActiveProfiles; @@ -31,6 +34,8 @@ import com.c4soft.quiz.domain.QuestionRepository; import com.c4soft.quiz.domain.Quiz; import com.c4soft.quiz.domain.QuizRepository; +import com.c4soft.quiz.feign.KeycloakAdminApiClient; +import com.c4soft.quiz.feign.KeycloakAdminApiClient.UserRepresentation; import com.c4soft.quiz.web.dto.ChoiceUpdateDto; import com.c4soft.quiz.web.dto.QuestionUpdateDto; import com.c4soft.quiz.web.dto.QuizUpdateDto; @@ -47,138 +52,159 @@ @AutoConfigureMockMvc @Import(AddonsWebmvcTestConf.class) class QuizApiApplicationTest { - @Autowired - QuizRepository quizRepo; - - @Autowired - QuestionRepository questionRepo; - - @Autowired - ChoiceRepository choiceRepo; - - @Autowired - MockMvcSupport api; - - @BeforeEach - void setUp() { - quizRepo.save(Fixtures.openIdTraingQuiz()); - } - - @Test - @WithJwt("ch4mp.json") - void givenUserIsCh4mp_whenGoingThroughQuizNominalCrudOperations_thenOk() - throws UnsupportedEncodingException, Exception { - var actual = parse(api.get("/quizzes").andExpect(status().isOk()).andReturn(), JSONArray.class); - assertEquals(1L, actual.size()); - assertEquals("OAuth2 and OpenID in web echosystem with Spring", ((JSONObject) actual.get(0)).get("title")); - - api.put(new QuizUpdateDto("Updated title"), "/quizzes/{quiz-id}", ((JSONObject) actual.get(0)).get("id")) - .andExpect(status().isAccepted()); - - final var quiz1Id = api.post(new QuizUpdateDto("Second Quiz"), "/quizzes").andExpect(status().isCreated()) - .andReturn().getResponse().getHeader(HttpHeaders.LOCATION); - api.get("/quizzes").andExpect(status().isOk()) - .andExpect(jsonPath("$.*.title", hasItems("Updated title", "Second Quiz"))); - - final var question10Id = api - .post(new QuestionUpdateDto("machin", "truc"), "/quizzes/{quiz-id}/questions", quiz1Id) - .andExpect(status().isCreated()).andReturn().getResponse().getHeader(HttpHeaders.LOCATION); - final var question11Id = api - .post(new QuestionUpdateDto("bidule", "chode"), "/quizzes/{quiz-id}/questions", quiz1Id) - .andExpect(status().isCreated()).andReturn().getResponse().getHeader(HttpHeaders.LOCATION); - api.get("/quizzes/{quiz-id}", quiz1Id).andExpect(status().isOk()) - .andExpect(jsonPath("$.questions.*.label", hasItems("machin", "bidule"))); - - api.put(List.of(question11Id, question10Id), "/quizzes/{quiz-id}/questions", quiz1Id) - .andExpect(status().isAccepted()); - api.put(new QuestionUpdateDto("What is the answer to machin?", "The answer is truc."), - "/quizzes/{quiz-id}/questions/{question-id}", quiz1Id, question10Id).andExpect(status().isAccepted()); - api.get("/quizzes/{quiz-id}", quiz1Id).andExpect(status().isOk()) - .andExpect(jsonPath("$.questions[0].label", is("bidule"))) - .andExpect(jsonPath("$.questions[1].label", is("What is the answer to machin?"))); - - final var choice100Id = api - .post(new ChoiceUpdateDto("truc", true), "/quizzes/{quiz-id}/questions/{question-id}/choices", quiz1Id, - question10Id) - .andExpect(status().isCreated()).andReturn().getResponse().getHeader(HttpHeaders.LOCATION); - final var choice101Id = api - .post(new ChoiceUpdateDto("chouette", false), "/quizzes/{quiz-id}/questions/{question-id}/choices", - quiz1Id, question10Id) - .andExpect(status().isCreated()).andReturn().getResponse().getHeader(HttpHeaders.LOCATION); - api.get("/quizzes/{quiz-id}", quiz1Id).andExpect(status().isOk()) - .andExpect(jsonPath("$.questions[1].choices.*.label", hasItems("truc", "chouette"))); - api.put(new ChoiceUpdateDto("chose", true), "/quizzes/{quiz-id}/questions/{question-id}/choices/{choice-id}", - quiz1Id, question10Id, choice101Id).andExpect(status().isAccepted()); - api.get("/quizzes/{quiz-id}", quiz1Id).andExpect(status().isOk()) - .andExpect(jsonPath("$.questions[1].choices.*.label", hasItems("truc", "chose"))); - - api.delete("/quizzes/{quiz-id}/questions/{question-id}/choices/{choice-id}", quiz1Id, question10Id, choice100Id) - .andExpect(status().isAccepted()); - api.get("/quizzes/{quiz-id}", quiz1Id).andExpect(status().isOk()) - .andExpect(jsonPath("$.questions[1].choices.*.label", hasSize(1))); - - api.delete("/quizzes/{quiz-id}/questions/{question-id}", quiz1Id, question10Id) - .andExpect(status().isAccepted()); - api.get("/quizzes/{quiz-id}", quiz1Id).andExpect(status().isOk()) - .andExpect(jsonPath("$.questions.*.label", hasSize(1))); - - api.delete("/quizzes/{quiz-id}", quiz1Id).andExpect(status().isAccepted()); - api.get("/quizzes").andExpect(status().isOk()).andExpect(jsonPath("$", hasSize(1))); - api.get("/quizzes/{quiz-id}", ((JSONObject) actual.get(0)).get("id")).andExpect(status().isOk()) - .andExpect(jsonPath("$.title", is("Updated title"))); - api.get("/quizzes/{quiz-id}", quiz1Id).andExpect(status().isNotFound()); - } - - @Test - @WithJwt("tonton-pirate.json") - void givenUserIsATrainee_whenGoingThroughSkillTestNominalOperations_thenOk() - throws UnsupportedEncodingException, Exception { - var quiz = (JSONObject) parse(api.get("/quizzes").andExpect(status().isOk()).andReturn(), JSONArray.class) - .get(0); - final var quizId = Long.valueOf(quiz.get("id").toString()); - - final var worstPossibleAnswer = new SkillTestDto(quizId, new ArrayList<>()); - final var perfectAnswer = new SkillTestDto(Long.valueOf(quiz.get("id").toString()), new ArrayList<>()); - for (var q : (JSONArray) quiz.get("questions")) { - final var question = (JSONObject) q; - final var questionId = Long.valueOf(question.get("questionId").toString()); - final var questionBestAnswer = new SkillTestQuestionDto(questionId, new ArrayList<>()); - final var questionWorstAnswer = new SkillTestQuestionDto(questionId, new ArrayList<>()); - for (var c : (JSONArray) question.get("choices")) { - final var choice = (JSONObject) c; - final var choiceId = Long.valueOf(choice.get("choiceId").toString()); - if ((Boolean) choice.get("isGood")) { - questionBestAnswer.choices().add(choiceId); - } else { - questionWorstAnswer.choices().add(choiceId); - } - } - perfectAnswer.questions().add(questionBestAnswer); - worstPossibleAnswer.questions().add(questionWorstAnswer); + @Autowired + QuizRepository quizRepo; + + @Autowired + QuestionRepository questionRepo; + + @Autowired + ChoiceRepository choiceRepo; + + @Autowired + MockMvcSupport api; + + @MockBean + KeycloakAdminApiClient keycloakAdminApi; + + @BeforeEach + void setUp() { + quizRepo.save(Fixtures.openIdTraingQuiz()); } - api.put(worstPossibleAnswer, "/skill-tests").andExpect(status().isAccepted()); - api.perform(get("/skill-tests").param("quizId", quizId.toString()).param("traineeName", "tonton-pirate")) - .andExpect(status().isOk()).andExpect(jsonPath("$[0].score", is(-50.0))); + @Test + @WithJwt("ch4mp.json") + void givenUserIsCh4mp_whenGoingThroughQuizNominalCrudOperations_thenOk() throws UnsupportedEncodingException, Exception { + // Database is initialized with 1 quiz + var actual = parse(api.get("/quizzes").andExpect(status().isOk()).andReturn(), JSONArray.class); + assertEquals(1L, actual.size()); + assertEquals("OAuth2 and OpenID in web echosystem with Spring", ((JSONObject) actual.get(0)).get("title")); + + // Create a draft from existing quiz + final var draftId = api.post(null, "/quizzes/{quiz-id}/draft", ((JSONObject) actual.get(0)).get("id")).andExpect(status().isCreated()).andReturn() + .getResponse().getHeader(HttpHeaders.LOCATION); + actual = parse(api.get("/quizzes").andExpect(status().isOk()).andReturn(), JSONArray.class); + assertEquals(2L, actual.size()); + + // Update draft title + api.put(new QuizUpdateDto("Updated title", true, true, true, false), "/quizzes/{quiz-id}", draftId).andExpect(status().isAccepted()); + + // Submit draft to moderation (with auto-publication as ch4mp is both trainer and moderator + api.put(null, "/quizzes/{quiz-id}/submit", draftId).andExpect(status().isAccepted()); + actual = parse(api.get("/quizzes").andExpect(status().isOk()).andReturn(), JSONArray.class); + assertEquals(2L, actual.size()); + assertEquals(1L, actual.stream().filter(q -> (boolean) ((JSONObject) q).get("isPublished")).toList().size()); + + // Create a new quiz + final var quiz1Id = api.post(new QuizUpdateDto("Second Quiz", true, true, true, false), "/quizzes").andExpect(status().isCreated()).andReturn().getResponse() + .getHeader(HttpHeaders.LOCATION); + api.get("/quizzes?titleLike=second").andExpect(status().isOk()).andExpect(jsonPath("$.*.title", hasItems("Second Quiz"))); + + // Add 2 questions to the new quiz + final var question10Id = api.post(new QuestionUpdateDto("machin", "truc"), "/quizzes/{quiz-id}/questions", quiz1Id).andExpect(status().isCreated()) + .andReturn().getResponse().getHeader(HttpHeaders.LOCATION); + final var question11Id = api.post(new QuestionUpdateDto("bidule", "chode"), "/quizzes/{quiz-id}/questions", quiz1Id).andExpect(status().isCreated()) + .andReturn().getResponse().getHeader(HttpHeaders.LOCATION); + api.get("/quizzes/{quiz-id}", quiz1Id).andExpect(status().isOk()).andExpect(jsonPath("$.questions.*.label", hasItems("machin", "bidule"))); + + // Reverse questions order + api.put(List.of(question11Id, question10Id), "/quizzes/{quiz-id}/questions", quiz1Id).andExpect(status().isAccepted()); + + // Update the 1st question title and comment + api.put( + new QuestionUpdateDto("What is the answer to machin?", "The answer is truc."), + "/quizzes/{quiz-id}/questions/{question-id}", + quiz1Id, + question10Id).andExpect(status().isAccepted()); + api.get("/quizzes/{quiz-id}", quiz1Id).andExpect(status().isOk()).andExpect(jsonPath("$.questions[0].label", is("bidule"))) + .andExpect(jsonPath("$.questions[1].label", is("What is the answer to machin?"))); + + // Add 2 choices + final var choice100Id = api.post(new ChoiceUpdateDto("truc", true), "/quizzes/{quiz-id}/questions/{question-id}/choices", quiz1Id, question10Id) + .andExpect(status().isCreated()).andReturn().getResponse().getHeader(HttpHeaders.LOCATION); + final var choice101Id = api.post(new ChoiceUpdateDto("chouette", false), "/quizzes/{quiz-id}/questions/{question-id}/choices", quiz1Id, question10Id) + .andExpect(status().isCreated()).andReturn().getResponse().getHeader(HttpHeaders.LOCATION); + api.get("/quizzes/{quiz-id}", quiz1Id).andExpect(status().isOk()).andExpect(jsonPath("$.questions[1].choices.*.label", hasItems("truc", "chouette"))); + api.put(new ChoiceUpdateDto("chose", true), "/quizzes/{quiz-id}/questions/{question-id}/choices/{choice-id}", quiz1Id, question10Id, choice101Id) + .andExpect(status().isAccepted()); + api.get("/quizzes/{quiz-id}", quiz1Id).andExpect(status().isOk()).andExpect(jsonPath("$.questions[1].choices.*.label", hasItems("truc", "chose"))); + + // Submit draft to moderation (with auto-publication as ch4mp is both trainer and moderator + api.put(null, "/quizzes/{quiz-id}/submit", quiz1Id).andExpect(status().isAccepted()); + actual = parse(api.get("/quizzes").andExpect(status().isOk()).andReturn(), JSONArray.class); + assertEquals(3L, actual.size()); + assertEquals(2L, actual.stream().filter(q -> (boolean) ((JSONObject) q).get("isPublished")).toList().size()); + + // Delete a question + api.delete("/quizzes/{quiz-id}/questions/{question-id}/choices/{choice-id}", quiz1Id, question10Id, choice100Id).andExpect(status().isAccepted()); + api.get("/quizzes/{quiz-id}", quiz1Id).andExpect(status().isOk()).andExpect(jsonPath("$.questions[1].choices.*.label", hasSize(1))); + + // Delete the other question + api.delete("/quizzes/{quiz-id}/questions/{question-id}", quiz1Id, question10Id).andExpect(status().isAccepted()); + api.get("/quizzes/{quiz-id}", quiz1Id).andExpect(status().isOk()).andExpect(jsonPath("$.questions.*.label", hasSize(1))); + + // Delete the new quiz + api.delete("/quizzes/{quiz-id}", quiz1Id).andExpect(status().isAccepted()); + api.get("/quizzes").andExpect(status().isOk()).andExpect(jsonPath("$", hasSize(2))); + api.get("/quizzes/{quiz-id}", draftId).andExpect(status().isOk()).andExpect(jsonPath("$.title", is("Updated title"))); + api.get("/quizzes/{quiz-id}", quiz1Id).andExpect(status().isNotFound()); + } + + @Test + @WithJwt("tonton-pirate.json") + void givenUserIsATrainee_whenGoingThroughSkillTestNominalOperations_thenOk() throws UnsupportedEncodingException, Exception { + var quiz = (JSONObject) parse(api.get("/quizzes").andExpect(status().isOk()).andReturn(), JSONArray.class).get(0); + final var quizId = Long.valueOf(quiz.get("id").toString()); + + final var worstPossibleAnswer = new SkillTestDto(quizId, new ArrayList<>()); + final var perfectAnswer = new SkillTestDto(quizId, new ArrayList<>()); + for (var q : (JSONArray) quiz.get("questions")) { + final var question = (JSONObject) q; + final var questionId = Long.valueOf(question.get("questionId").toString()); + final var questionBestAnswer = new SkillTestQuestionDto(questionId, new ArrayList<>()); + final var questionWorstAnswer = new SkillTestQuestionDto(questionId, new ArrayList<>()); + for (var c : (JSONArray) question.get("choices")) { + final var choice = (JSONObject) c; + final var choiceId = Long.valueOf(choice.get("choiceId").toString()); + if ((Boolean) choice.get("isGood")) { + questionBestAnswer.choices().add(choiceId); + } else { + questionWorstAnswer.choices().add(choiceId); + } + } + perfectAnswer.questions().add(questionBestAnswer); + worstPossibleAnswer.questions().add(questionWorstAnswer); + } - api.put(perfectAnswer, "/skill-tests").andExpect(status().isAccepted()); - api.perform(get("/skill-tests").param("quizId", quizId.toString()).param("traineeName", "tonton-pirate")) - .andExpect(status().isOk()).andExpect(jsonPath("$[0].score", is(100.0))); + api.put(worstPossibleAnswer, "/skill-tests").andExpect(status().isOk()).andExpect(jsonPath("$.score", is(-50.0))); + when(keycloakAdminApi.getUser("tonton-pirate", true)).thenReturn(List.of(new UserRepresentation(Map.of("email", "tonton-pirate@c4-soft.com")))); + api.perform(get("/skill-tests/{quizId}/{traineeName}", quizId.toString(), "tonton-pirate")).andExpect(status().isOk()) + .andExpect(jsonPath("$.score", is(-50.0))); - } + api.put(perfectAnswer, "/skill-tests").andExpect(status().isOk()).andExpect(jsonPath("$.score", is(100.0))); + api.perform(get("/skill-tests/{quizId}/{traineeName}", quizId.toString(), "tonton-pirate")).andExpect(status().isOk()) + .andExpect(jsonPath("$.score", is(100.0))); - private T parse(MvcResult result, Class clazz) throws UnsupportedEncodingException, ParseException { - return new JSONParser(JSONParser.MODE_PERMISSIVE).parse(result.getResponse().getContentAsString(), clazz); - } + } - public static class Fixtures { - static final String former1 = "ch4mp"; + private T parse(MvcResult result, Class clazz) throws UnsupportedEncodingException, ParseException { + return new JSONParser(JSONParser.MODE_PERMISSIVE).parse(result.getResponse().getContentAsString(), clazz); + } - public static Quiz openIdTraingQuiz() { - return new Quiz("OAuth2 and OpenID in web echosystem with Spring", former1, - new Question("Question 1", 0, "Good is right", new Choice("good", true), new Choice("bad", false)), - new Question("Question 2", 1, "Bad is wrong", new Choice("bad", false), new Choice("good", true))); + public static class Fixtures { + static final String trainer1 = "ch4mp"; + static final String moderator1 = "ch4mp"; + + public static Quiz openIdTraingQuiz() { + final var quiz = new Quiz( + "OAuth2 and OpenID in web echosystem with Spring", + trainer1, + new Question("Question 1", 0, "Good is right", new Choice("good", true), new Choice("bad", false)), + new Question("Question 2", 1, "Bad is wrong", new Choice("bad", false), new Choice("good", true))); + quiz.setModeratedBy(moderator1); + quiz.setIsPublished(true); + return quiz; + } } - } } diff --git a/api/quiz-api/src/test/resources/ch4mp.json b/api/quiz-api/src/test/resources/ch4mp.json index 5030385..a5d6aad 100644 --- a/api/quiz-api/src/test/resources/ch4mp.json +++ b/api/quiz-api/src/test/resources/ch4mp.json @@ -2,20 +2,9 @@ "preferred_username": "ch4mp", "realm_access": { "roles": [ - "USER_ROLES_EDITOR", "AUTHOR" + "trainer", + "moderator" ] }, - "resource_access": { - "quiz": { - "roles": [ - "former" - ] - }, - "spring-addons": { - "roles": [ - "nice" - ] - } - }, "scope": "openid email" } \ No newline at end of file