From 6af1b2917abaa87524944a76eb150d0be5b0adad Mon Sep 17 00:00:00 2001 From: Ben Stein <115497763+sei-bstein@users.noreply.github.com> Date: Mon, 1 Apr 2024 13:56:55 -0400 Subject: [PATCH] v3.18.0 (#180) * Rebase to next * Resolved issue that caused an automatic refresh on external game load. * Fix stray refresh bug for external games * Remove log * Remove automatic iframe refresh * Use behaviorsubject in the api status interceptor rather than startWith * Copy challenge IDs on external game admin. Mild restyling. * Fixed an issue that caused game card images to fail to be removed when requested. * Updates to signalR architecture. Improve spinner readability. * Only respond to sync start events for the current game in the session start controls. * Finalize SignalR GameHub implementation * Audit fix * Update reset team endpoint paylod * All hidden challenge specs. * Bump katex from 0.16.4 to 0.16.10 Bumps [katex](https://github.com/KaTeX/KaTeX) from 0.16.4 to 0.16.10. - [Release notes](https://github.com/KaTeX/KaTeX/releases) - [Changelog](https://github.com/KaTeX/KaTeX/blob/main/CHANGELOG.md) - [Commits](https://github.com/KaTeX/KaTeX/compare/v0.16.4...v0.16.10) --- updated-dependencies: - dependency-name: katex dependency-type: indirect ... Signed-off-by: dependabot[bot] * Add player readiness counts to external game deploy admin. Resolves GBAPI #405. * Added simple filtering/search to active team/challenge modals * Fix various unenroll/reset session bugs. Readying/unreading a player from Players now applies the condition to their entire team. * Clean up button text --------- Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- package-lock.json | 617 ++++++++---------- patch-it | 46 ++ .../admin-player-session.component.html | 9 +- .../src/app/admin/admin.module.ts | 4 + .../active-challenges-modal.component.html | 141 ++-- .../active-challenges-modal.component.ts | 23 +- .../active-teams-modal.component.html | 27 +- .../active-teams-modal.component.ts | 19 + .../admin-enroll-team-modal.component.html | 2 +- .../challenge-spec-editor.component.html | 17 +- .../challenge-spec-editor.component.ts | 4 +- ...e-admin-player-context-menu.component.html | 3 +- ...ame-admin-player-context-menu.component.ts | 11 +- ...-game-admin-team-context-menu.component.ts | 3 +- .../external-game-admin.component.html | 52 +- .../external-game-admin.component.scss | 56 +- .../external-game-admin.component.ts | 12 +- .../team-admin-context-menu.component.html | 33 +- .../team-admin-context-menu.component.ts | 14 +- .../game-designer.component.html | 2 - .../game-editor/game-editor.component.html | 6 +- .../game-editor/game-editor.component.ts | 19 +- .../game-mapper/game-mapper.component.html | 4 +- .../game-mapper/game-mapper.component.scss | 10 +- .../game-mapper/game-mapper.component.ts | 13 +- ...nal-game-player-status-to-friendly.pipe.ts | 4 +- ...game-player-status-to-status-light.pipe.ts | 4 +- .../sync-start-game-state-description.pipe.ts | 25 + ...sync-start-team-player-ready-count.pipe.ts | 13 + .../player-registrar.component.html | 2 +- .../player-registrar.component.ts | 69 +- .../src/app/api-status.interceptor.ts | 8 +- .../gameboard-ui/src/app/api/board-models.ts | 6 - .../gameboard-ui/src/app/api/game.service.ts | 16 +- .../gameboard-ui/src/app/api/spec-models.ts | 1 + .../gameboard-ui/src/app/api/team.service.ts | 12 +- .../gameboard-ui/src/app/api/teams.models.ts | 4 +- .../gameboard-ui/src/app/app.component.html | 8 +- .../gameboard-ui/src/app/app.component.scss | 4 - .../gameboard-ui/src/app/app.component.ts | 2 +- .../gameboard-signalr-hubs.component.html | 14 +- .../gameboard-signalr-hubs.component.scss | 11 + .../gameboard-signalr-hubs.component.ts | 67 +- .../components/spinner/spinner.component.ts | 19 +- .../status-light/status-light.component.ts | 4 +- .../whats-this/whats-this.component.ts | 16 + .../gameboard-ui/src/app/core/core.module.ts | 2 + .../directives/copy-on-click.directive.ts | 2 +- .../refresh-iframe-on-reconnect.directive.ts | 7 + ...coreboard-team-detail-modal.component.html | 8 +- .../scoreboard/scoreboard.component.html | 6 +- .../session-start-controls.component.html | 24 +- .../session-start-controls.component.ts | 18 +- .../gameboard-ui/src/app/game/game.module.ts | 4 +- .../external-game-loading-page.component.ts | 13 +- .../external-game-page.component.html | 1 - .../pages/game-page/game-page.component.ts | 33 +- .../pipes/manual-bonuses-to-tooltip.pipe.ts | 4 +- .../player-session.component.ts | 2 +- .../app/services/signalR/game-hub.models.ts | 15 +- .../app/services/signalR/game-hub.service.ts | 63 +- .../app/services/signalR/signalr.service.ts | 35 +- .../services/signalR/support-hub.service.ts | 4 - .../sponsor-with-children-picker.component.ts | 7 +- .../admin-system-notifications.component.html | 8 +- ...t-system-notification-modal.component.html | 4 +- .../src/app/utility/services/toast.service.ts | 23 +- projects/gameboard-ui/src/scss/_toastify.scss | 22 +- projects/gameboard-ui/src/styles.scss | 37 +- 69 files changed, 1014 insertions(+), 784 deletions(-) create mode 100644 patch-it create mode 100644 projects/gameboard-ui/src/app/admin/pipes/sync-start-game-state-description.pipe.ts create mode 100644 projects/gameboard-ui/src/app/admin/pipes/sync-start-team-player-ready-count.pipe.ts create mode 100644 projects/gameboard-ui/src/app/core/components/whats-this/whats-this.component.ts diff --git a/package-lock.json b/package-lock.json index e64c114e..d17b5642 100644 --- a/package-lock.json +++ b/package-lock.json @@ -119,15 +119,15 @@ "license": "0BSD" }, "node_modules/@angular-devkit/build-angular": { - "version": "15.2.10", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-15.2.10.tgz", - "integrity": "sha512-3pCPVEJilVwHIJC6Su1/PIEqvFfU1Lxew9yItxX4s6dud8HY+fuKrsDnao4NNMFNqCLqL4el5QbSBKnnpWH1sg==", + "version": "15.2.11", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-15.2.11.tgz", + "integrity": "sha512-MnpVCJdk5jHuK7CH/cTcRT0JQkkKkRTEV3WTyOUhTm0O3PlKwvTM6/Sner+zyuhKyw5VFBBMypHh59aTUDEZ1A==", "dev": true, "dependencies": { "@ampproject/remapping": "2.2.0", - "@angular-devkit/architect": "0.1502.10", - "@angular-devkit/build-webpack": "0.1502.10", - "@angular-devkit/core": "15.2.10", + "@angular-devkit/architect": "0.1502.11", + "@angular-devkit/build-webpack": "0.1502.11", + "@angular-devkit/core": "15.2.11", "@babel/core": "7.20.12", "@babel/generator": "7.20.14", "@babel/helper-annotate-as-pure": "7.18.6", @@ -139,7 +139,7 @@ "@babel/runtime": "7.20.13", "@babel/template": "7.20.7", "@discoveryjs/json-ext": "0.5.7", - "@ngtools/webpack": "15.2.10", + "@ngtools/webpack": "15.2.11", "ansi-colors": "4.1.3", "autoprefixer": "10.4.13", "babel-loader": "9.1.2", @@ -180,7 +180,7 @@ "tree-kill": "1.2.2", "tslib": "2.5.0", "webpack": "5.76.1", - "webpack-dev-middleware": "6.0.1", + "webpack-dev-middleware": "6.1.2", "webpack-dev-server": "4.11.1", "webpack-merge": "5.8.0", "webpack-subresource-integrity": "5.1.0" @@ -229,12 +229,12 @@ } }, "node_modules/@angular-devkit/build-angular/node_modules/@angular-devkit/architect": { - "version": "0.1502.10", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1502.10.tgz", - "integrity": "sha512-S8lN73WYCfpEpw1Q41ZcUinw7JfDeSM8LyGs797OVshnW75QcOkOecWj/3CKR23G44IgFrHN6sqtzWxKmMxLig==", + "version": "0.1502.11", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1502.11.tgz", + "integrity": "sha512-+hkG5UjIaKMRdo6SFLNQs+Cv7yAVeN8ijfDwI2z/mp7/otowuSEy+H3Tii195jfJ8TQ+y1B7svnx2D6O7oOYbQ==", "dev": true, "dependencies": { - "@angular-devkit/core": "15.2.10", + "@angular-devkit/core": "15.2.11", "rxjs": "6.6.7" }, "engines": { @@ -244,9 +244,9 @@ } }, "node_modules/@angular-devkit/build-angular/node_modules/@angular-devkit/core": { - "version": "15.2.10", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-15.2.10.tgz", - "integrity": "sha512-bFPm7wjvfBds9km2rCJxUhzkqe4h3h/199yJtzC1bNvwRr2LMHvtyoQAzftda+gs7Ulqac5wzUEZX/cVV3WrsA==", + "version": "15.2.11", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-15.2.11.tgz", + "integrity": "sha512-zd6QelJ8pOPvz6TsehR0JqixjDjzgEOkKywBJBuwNXY+Nw3MJGayJeWS0UgC+Gk+LoTkpI21RoyaYELkAmD/tw==", "dev": true, "dependencies": { "ajv": "8.12.0", @@ -271,8 +271,9 @@ }, "node_modules/@angular-devkit/build-angular/node_modules/rxjs": { "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", "dev": true, - "license": "Apache-2.0", "dependencies": { "tslib": "^1.9.0" }, @@ -282,16 +283,17 @@ }, "node_modules/@angular-devkit/build-angular/node_modules/rxjs/node_modules/tslib": { "version": "1.14.1", - "dev": true, - "license": "0BSD" + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true }, "node_modules/@angular-devkit/build-webpack": { - "version": "0.1502.10", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1502.10.tgz", - "integrity": "sha512-55b9WZIGU4DNgiIV2lkkN6iQxJrgWY5CDaNu0kJC/qazotJgBbcN/8jgBx2DD8HNE1V3iXxWk66pt1h946Po+Q==", + "version": "0.1502.11", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1502.11.tgz", + "integrity": "sha512-OTONIRp770Jfems4+cULmtoeSzjnpx5UjV2EazojnhRXXBSJMWRMPvwD2QvQl9UO/6eOV3d2mgmP2xOZgc/D6w==", "dev": true, "dependencies": { - "@angular-devkit/architect": "0.1502.10", + "@angular-devkit/architect": "0.1502.11", "rxjs": "6.6.7" }, "engines": { @@ -305,12 +307,12 @@ } }, "node_modules/@angular-devkit/build-webpack/node_modules/@angular-devkit/architect": { - "version": "0.1502.10", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1502.10.tgz", - "integrity": "sha512-S8lN73WYCfpEpw1Q41ZcUinw7JfDeSM8LyGs797OVshnW75QcOkOecWj/3CKR23G44IgFrHN6sqtzWxKmMxLig==", + "version": "0.1502.11", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1502.11.tgz", + "integrity": "sha512-+hkG5UjIaKMRdo6SFLNQs+Cv7yAVeN8ijfDwI2z/mp7/otowuSEy+H3Tii195jfJ8TQ+y1B7svnx2D6O7oOYbQ==", "dev": true, "dependencies": { - "@angular-devkit/core": "15.2.10", + "@angular-devkit/core": "15.2.11", "rxjs": "6.6.7" }, "engines": { @@ -320,9 +322,9 @@ } }, "node_modules/@angular-devkit/build-webpack/node_modules/@angular-devkit/core": { - "version": "15.2.10", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-15.2.10.tgz", - "integrity": "sha512-bFPm7wjvfBds9km2rCJxUhzkqe4h3h/199yJtzC1bNvwRr2LMHvtyoQAzftda+gs7Ulqac5wzUEZX/cVV3WrsA==", + "version": "15.2.11", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-15.2.11.tgz", + "integrity": "sha512-zd6QelJ8pOPvz6TsehR0JqixjDjzgEOkKywBJBuwNXY+Nw3MJGayJeWS0UgC+Gk+LoTkpI21RoyaYELkAmD/tw==", "dev": true, "dependencies": { "ajv": "8.12.0", @@ -2854,9 +2856,9 @@ } }, "node_modules/@leichtgewicht/ip-codec": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", - "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", + "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", "dev": true }, "node_modules/@microsoft/signalr": { @@ -2913,9 +2915,9 @@ } }, "node_modules/@ngtools/webpack": { - "version": "15.2.10", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-15.2.10.tgz", - "integrity": "sha512-ZExB4rKh/Saad31O/Ofd2XvRuILuCNTYs0+qJL697Be2pzeewvzBhE4Xe1Mm7Jg13aWSPeuIdzSGOqCdwxxxFQ==", + "version": "15.2.11", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-15.2.11.tgz", + "integrity": "sha512-yqp+FziuJ+wIVij4eTqfhuiTPNaG1PU8ukeGOdqkVH4nQMlmzs9UldXy1iYC/6swzn6XO/pkqisU3m/jxemMzA==", "dev": true, "engines": { "node": "^14.20.0 || ^16.13.0 || >=18.10.0", @@ -3173,9 +3175,9 @@ } }, "node_modules/@types/body-parser": { - "version": "1.19.3", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.3.tgz", - "integrity": "sha512-oyl4jvAfTGX9Bt6Or4H9ni1Z447/tQuxnZsytsCaExKlmJiU8sFgnIBRzJUpKwB5eWn9HuBYlUlVA74q/yN0eQ==", + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", "dev": true, "dependencies": { "@types/connect": "*", @@ -3183,27 +3185,27 @@ } }, "node_modules/@types/bonjour": { - "version": "3.5.11", - "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.11.tgz", - "integrity": "sha512-isGhjmBtLIxdHBDl2xGwUzEM8AOyOvWsADWq7rqirdi/ZQoHnLWErHvsThcEzTX8juDRiZtzp2Qkv5bgNh6mAg==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz", + "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==", "dev": true, "dependencies": { "@types/node": "*" } }, "node_modules/@types/connect": { - "version": "3.4.36", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.36.tgz", - "integrity": "sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==", + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", "dev": true, "dependencies": { "@types/node": "*" } }, "node_modules/@types/connect-history-api-fallback": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.1.tgz", - "integrity": "sha512-iaQslNbARe8fctL5Lk+DsmgWOM83lM+7FzP0eQUJs1jd3kBE8NWqBTIT2S8SqQOJjxvt2eyIjpOuYeRXq2AdMw==", + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz", + "integrity": "sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==", "dev": true, "dependencies": { "@types/express-serve-static-core": "*", @@ -3247,9 +3249,9 @@ "license": "MIT" }, "node_modules/@types/express": { - "version": "4.17.19", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.19.tgz", - "integrity": "sha512-UtOfBtzN9OvpZPPbnnYunfjM7XCI4jyk1NvnFhTVz5krYAnW4o5DCoIekvms+8ApqhB4+9wSge1kBijdfTSmfg==", + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", "dev": true, "dependencies": { "@types/body-parser": "*", @@ -3259,9 +3261,9 @@ } }, "node_modules/@types/express-serve-static-core": { - "version": "4.17.37", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.37.tgz", - "integrity": "sha512-ZohaCYTgGFcOP7u6aJOhY9uIZQgZ2vxC2yWoArY+FeDXlqeH66ZVBjgvg+RLVAS/DWNq4Ap9ZXu1+SUQiiWYMg==", + "version": "4.17.43", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.43.tgz", + "integrity": "sha512-oaYtiBirUOPQGSWNGPWnzyAFJ0BP3cwvN4oWZQY+zUBwpVIGsKUkpBpSztp74drYcjavs7SKFZ4DX1V2QeN8rg==", "dev": true, "dependencies": { "@types/node": "*", @@ -3277,15 +3279,15 @@ "peer": true }, "node_modules/@types/http-errors": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.2.tgz", - "integrity": "sha512-lPG6KlZs88gef6aD85z3HNkztpj7w2R7HmR3gygjfXCQmsLloWNARFkMuzKiiY8FGdh1XDpgBdrSf4aKDiA7Kg==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", "dev": true }, "node_modules/@types/http-proxy": { - "version": "1.17.12", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.12.tgz", - "integrity": "sha512-kQtujO08dVtQ2wXAuSFfk9ASy3sug4+ogFR8Kd8UgP8PEuc1/G/8yjYRmp//PcDNJEUKOza/MrQu15bouEUCiw==", + "version": "1.17.14", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.14.tgz", + "integrity": "sha512-SSrD0c1OQzlFX7pGu1eXxSEjemej64aaNPRhhVYUGqXh0BtldAAx37MG8btcumvpgKyZp1F5Gn3JkktdxiFv6w==", "dev": true, "dependencies": { "@types/node": "*" @@ -3316,9 +3318,9 @@ "license": "MIT" }, "node_modules/@types/mime": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.3.tgz", - "integrity": "sha512-Ys+/St+2VF4+xuY6+kDIXGxbNRO0mesVg0bbxEfB97Od1Vjpjx9KD1qxs64Gcb3CWPirk9Xe+PT4YiiHQ9T+eg==", + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", "dev": true }, "node_modules/@types/node": { @@ -3326,21 +3328,30 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/node-forge": { + "version": "1.3.11", + "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz", + "integrity": "sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/parse-json": { "version": "4.0.0", "dev": true, "license": "MIT" }, "node_modules/@types/qs": { - "version": "6.9.8", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.8.tgz", - "integrity": "sha512-u95svzDlTysU5xecFNTgfFG5RUWu1A9P0VzgpcIiGZA9iraHOdSzcxMxQ55DyeRaGCSxQi7LxXDI4rzq/MYfdg==", + "version": "6.9.14", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.14.tgz", + "integrity": "sha512-5khscbd3SwWMhFqylJBLQ0zIu7c1K6Vz0uBIt915BI3zV0q1nfjRQD3RqSBcPaO6PHEF4ov/t9y89fSiyThlPA==", "dev": true }, "node_modules/@types/range-parser": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.5.tgz", - "integrity": "sha512-xrO9OoVPqFuYyR/loIHjnbvvyRZREYKLjxV4+dY6v3FQR3stQ9ZxIGkaclF7YhI9hfjpuTbu14hZEy94qKLtOA==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", "dev": true }, "node_modules/@types/retry": { @@ -3355,9 +3366,9 @@ "license": "MIT" }, "node_modules/@types/send": { - "version": "0.17.2", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.2.tgz", - "integrity": "sha512-aAG6yRf6r0wQ29bkS+x97BIs64ZLxeE/ARwyS6wrldMm3C1MdKwCcnnEwMC1slI8wuxJOpiUH9MioC0A0i+GJw==", + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", "dev": true, "dependencies": { "@types/mime": "^1", @@ -3365,18 +3376,18 @@ } }, "node_modules/@types/serve-index": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.2.tgz", - "integrity": "sha512-asaEIoc6J+DbBKXtO7p2shWUpKacZOoMBEGBgPG91P8xhO53ohzHWGCs4ScZo5pQMf5ukQzVT9fhX1WzpHihig==", + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz", + "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==", "dev": true, "dependencies": { "@types/express": "*" } }, "node_modules/@types/serve-static": { - "version": "1.15.3", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.3.tgz", - "integrity": "sha512-yVRvFsEMrv7s0lGhzrggJjNOSmZCdgCjw9xWrPr/kNNLp6FaDfMC1KaYl3TSJ0c58bECwNBMoQrZJ8hA8E1eFg==", + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz", + "integrity": "sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==", "dev": true, "dependencies": { "@types/http-errors": "*", @@ -3385,9 +3396,9 @@ } }, "node_modules/@types/sockjs": { - "version": "0.3.34", - "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.34.tgz", - "integrity": "sha512-R+n7qBFnm/6jinlteC9DBL5dGiDGjWAvjo4viUanpnc/dG1y7uDoacXPIQ/PQEg1fI912SMHIa014ZjRpvDw4g==", + "version": "0.3.36", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz", + "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==", "dev": true, "dependencies": { "@types/node": "*" @@ -3404,9 +3415,9 @@ "license": "MIT" }, "node_modules/@types/ws": { - "version": "8.5.7", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.7.tgz", - "integrity": "sha512-6UrLjiDUvn40CMrAubXuIVtj2PEfKDffJS7ychvnPU44j+KVeXmdHHTgqcM/dxLUTHxlXHiFM8Skmb8ozGdTnQ==", + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", + "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", "dev": true, "dependencies": { "@types/node": "*" @@ -4031,9 +4042,9 @@ } }, "node_modules/array-flatten": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", - "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "dev": true }, "node_modules/array-includes": { @@ -4315,13 +4326,11 @@ "license": "MIT" }, "node_modules/bonjour-service": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.1.1.tgz", - "integrity": "sha512-Z/5lQRMOG9k7W+FkeGTNjh7htqn/2LMnfOvBZ8pynNZCM9MwkQkI3zeI4oz09uWdcgmgHugVvBqxGg4VQJ5PCg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.2.1.tgz", + "integrity": "sha512-oSzCS2zV14bh2kji6vNe7vrpJYCHGvcZnlffFQ1MEoX/WOeQ/teD8SYWKR942OI3INjq8OMNJlbPK5LLLUxFDw==", "dev": true, "dependencies": { - "array-flatten": "^2.1.2", - "dns-equal": "^1.0.0", "fast-deep-equal": "^3.1.3", "multicast-dns": "^7.2.5" } @@ -4692,9 +4701,10 @@ } }, "node_modules/colorette": { - "version": "2.0.19", - "dev": true, - "license": "MIT" + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true }, "node_modules/commander": { "version": "8.3.0", @@ -5741,12 +5751,6 @@ "node": ">=8" } }, - "node_modules/dns-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", - "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==", - "dev": true - }, "node_modules/dns-packet": { "version": "5.6.1", "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", @@ -6914,17 +6918,17 @@ } }, "node_modules/express": { - "version": "4.18.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", - "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", + "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", "dev": true, "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.1", + "body-parser": "1.20.2", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.5.0", + "cookie": "0.6.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -6955,40 +6959,10 @@ "node": ">= 0.10.0" } }, - "node_modules/express/node_modules/array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "dev": true - }, - "node_modules/express/node_modules/body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", - "dev": true, - "dependencies": { - "bytes": "3.1.2", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.1", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8", - "npm": "1.2.8000 || >= 1.4.16" - } - }, "node_modules/express/node_modules/cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", "dev": true, "engines": { "node": ">= 0.6" @@ -7027,21 +7001,6 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, - "node_modules/express/node_modules/raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", - "dev": true, - "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/express/node_modules/statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -7333,9 +7292,10 @@ } }, "node_modules/fs-monkey": { - "version": "1.0.3", - "dev": true, - "license": "Unlicense" + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.5.tgz", + "integrity": "sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew==", + "dev": true }, "node_modules/fs.realpath": { "version": "1.0.0", @@ -7737,9 +7697,9 @@ } }, "node_modules/html-entities": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.4.0.tgz", - "integrity": "sha512-igBTJcNNNhvZFRtm8uA6xMY6xYleeDwn3PeBCkDz7tHttv4F2hsDI2aPgNERWzvRcNYHNT3ymRaQzllmXj4YsQ==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz", + "integrity": "sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==", "dev": true, "funding": [ { @@ -8981,15 +8941,16 @@ } }, "node_modules/katex": { - "version": "0.16.4", + "version": "0.16.10", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.10.tgz", + "integrity": "sha512-ZiqaC04tp2O5utMsl2TEZTXxa6WSC4yo0fv5ML++D3QZv/vx2Mct0mTlRx3O+uUkjfuAgOkzsCmq5MiUEsDDdA==", "funding": [ "https://opencollective.com/katex", "https://github.com/sponsors/katex" ], - "license": "MIT", "optional": true, "dependencies": { - "commander": "^8.0.0" + "commander": "^8.3.0" }, "bin": { "katex": "cli.js" @@ -9498,11 +9459,12 @@ } }, "node_modules/memfs": { - "version": "3.4.13", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", + "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", "dev": true, - "license": "Unlicense", "dependencies": { - "fs-monkey": "^1.0.3" + "fs-monkey": "^1.0.4" }, "engines": { "node": ">= 4.0.0" @@ -11739,11 +11701,12 @@ "dev": true }, "node_modules/selfsigned": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.1.1.tgz", - "integrity": "sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", + "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", "dev": true, "dependencies": { + "@types/node-forge": "^1.3.0", "node-forge": "^1" }, "engines": { @@ -13341,9 +13304,10 @@ } }, "node_modules/webpack-dev-middleware": { - "version": "6.0.1", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-6.1.2.tgz", + "integrity": "sha512-Wu+EHmX326YPYUpQLKmKbTyZZJIB8/n6R09pTmB03kJmnMsVPTo9COzHZFr01txwaCAuZvfBJE4ZCHRcKs5JaQ==", "dev": true, - "license": "MIT", "dependencies": { "colorette": "^2.0.10", "memfs": "^3.4.12", @@ -13360,6 +13324,11 @@ }, "peerDependencies": { "webpack": "^5.0.0" + }, + "peerDependenciesMeta": { + "webpack": { + "optional": true + } } }, "node_modules/webpack-dev-server": { @@ -13418,9 +13387,9 @@ } }, "node_modules/webpack-dev-server/node_modules/webpack-dev-middleware": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz", - "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==", + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz", + "integrity": "sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==", "dev": true, "dependencies": { "colorette": "^2.0.10", @@ -13441,9 +13410,9 @@ } }, "node_modules/webpack-dev-server/node_modules/ws": { - "version": "8.14.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz", - "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", + "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", "dev": true, "engines": { "node": ">=10.0.0" @@ -13830,15 +13799,15 @@ } }, "@angular-devkit/build-angular": { - "version": "15.2.10", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-15.2.10.tgz", - "integrity": "sha512-3pCPVEJilVwHIJC6Su1/PIEqvFfU1Lxew9yItxX4s6dud8HY+fuKrsDnao4NNMFNqCLqL4el5QbSBKnnpWH1sg==", + "version": "15.2.11", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-angular/-/build-angular-15.2.11.tgz", + "integrity": "sha512-MnpVCJdk5jHuK7CH/cTcRT0JQkkKkRTEV3WTyOUhTm0O3PlKwvTM6/Sner+zyuhKyw5VFBBMypHh59aTUDEZ1A==", "dev": true, "requires": { "@ampproject/remapping": "2.2.0", - "@angular-devkit/architect": "0.1502.10", - "@angular-devkit/build-webpack": "0.1502.10", - "@angular-devkit/core": "15.2.10", + "@angular-devkit/architect": "0.1502.11", + "@angular-devkit/build-webpack": "0.1502.11", + "@angular-devkit/core": "15.2.11", "@babel/core": "7.20.12", "@babel/generator": "7.20.14", "@babel/helper-annotate-as-pure": "7.18.6", @@ -13850,7 +13819,7 @@ "@babel/runtime": "7.20.13", "@babel/template": "7.20.7", "@discoveryjs/json-ext": "0.5.7", - "@ngtools/webpack": "15.2.10", + "@ngtools/webpack": "15.2.11", "ansi-colors": "4.1.3", "autoprefixer": "10.4.13", "babel-loader": "9.1.2", @@ -13892,26 +13861,26 @@ "tree-kill": "1.2.2", "tslib": "2.5.0", "webpack": "5.76.1", - "webpack-dev-middleware": "6.0.1", + "webpack-dev-middleware": "6.1.2", "webpack-dev-server": "4.11.1", "webpack-merge": "5.8.0", "webpack-subresource-integrity": "5.1.0" }, "dependencies": { "@angular-devkit/architect": { - "version": "0.1502.10", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1502.10.tgz", - "integrity": "sha512-S8lN73WYCfpEpw1Q41ZcUinw7JfDeSM8LyGs797OVshnW75QcOkOecWj/3CKR23G44IgFrHN6sqtzWxKmMxLig==", + "version": "0.1502.11", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1502.11.tgz", + "integrity": "sha512-+hkG5UjIaKMRdo6SFLNQs+Cv7yAVeN8ijfDwI2z/mp7/otowuSEy+H3Tii195jfJ8TQ+y1B7svnx2D6O7oOYbQ==", "dev": true, "requires": { - "@angular-devkit/core": "15.2.10", + "@angular-devkit/core": "15.2.11", "rxjs": "6.6.7" } }, "@angular-devkit/core": { - "version": "15.2.10", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-15.2.10.tgz", - "integrity": "sha512-bFPm7wjvfBds9km2rCJxUhzkqe4h3h/199yJtzC1bNvwRr2LMHvtyoQAzftda+gs7Ulqac5wzUEZX/cVV3WrsA==", + "version": "15.2.11", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-15.2.11.tgz", + "integrity": "sha512-zd6QelJ8pOPvz6TsehR0JqixjDjzgEOkKywBJBuwNXY+Nw3MJGayJeWS0UgC+Gk+LoTkpI21RoyaYELkAmD/tw==", "dev": true, "requires": { "ajv": "8.12.0", @@ -13923,6 +13892,8 @@ }, "rxjs": { "version": "6.6.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.7.tgz", + "integrity": "sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==", "dev": true, "requires": { "tslib": "^1.9.0" @@ -13930,6 +13901,8 @@ "dependencies": { "tslib": { "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", "dev": true } } @@ -13937,29 +13910,29 @@ } }, "@angular-devkit/build-webpack": { - "version": "0.1502.10", - "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1502.10.tgz", - "integrity": "sha512-55b9WZIGU4DNgiIV2lkkN6iQxJrgWY5CDaNu0kJC/qazotJgBbcN/8jgBx2DD8HNE1V3iXxWk66pt1h946Po+Q==", + "version": "0.1502.11", + "resolved": "https://registry.npmjs.org/@angular-devkit/build-webpack/-/build-webpack-0.1502.11.tgz", + "integrity": "sha512-OTONIRp770Jfems4+cULmtoeSzjnpx5UjV2EazojnhRXXBSJMWRMPvwD2QvQl9UO/6eOV3d2mgmP2xOZgc/D6w==", "dev": true, "requires": { - "@angular-devkit/architect": "0.1502.10", + "@angular-devkit/architect": "0.1502.11", "rxjs": "6.6.7" }, "dependencies": { "@angular-devkit/architect": { - "version": "0.1502.10", - "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1502.10.tgz", - "integrity": "sha512-S8lN73WYCfpEpw1Q41ZcUinw7JfDeSM8LyGs797OVshnW75QcOkOecWj/3CKR23G44IgFrHN6sqtzWxKmMxLig==", + "version": "0.1502.11", + "resolved": "https://registry.npmjs.org/@angular-devkit/architect/-/architect-0.1502.11.tgz", + "integrity": "sha512-+hkG5UjIaKMRdo6SFLNQs+Cv7yAVeN8ijfDwI2z/mp7/otowuSEy+H3Tii195jfJ8TQ+y1B7svnx2D6O7oOYbQ==", "dev": true, "requires": { - "@angular-devkit/core": "15.2.10", + "@angular-devkit/core": "15.2.11", "rxjs": "6.6.7" } }, "@angular-devkit/core": { - "version": "15.2.10", - "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-15.2.10.tgz", - "integrity": "sha512-bFPm7wjvfBds9km2rCJxUhzkqe4h3h/199yJtzC1bNvwRr2LMHvtyoQAzftda+gs7Ulqac5wzUEZX/cVV3WrsA==", + "version": "15.2.11", + "resolved": "https://registry.npmjs.org/@angular-devkit/core/-/core-15.2.11.tgz", + "integrity": "sha512-zd6QelJ8pOPvz6TsehR0JqixjDjzgEOkKywBJBuwNXY+Nw3MJGayJeWS0UgC+Gk+LoTkpI21RoyaYELkAmD/tw==", "dev": true, "requires": { "ajv": "8.12.0", @@ -15545,9 +15518,9 @@ } }, "@leichtgewicht/ip-codec": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", - "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.5.tgz", + "integrity": "sha512-Vo+PSpZG2/fmgmiNzYK9qWRh8h/CHrwD0mo1h1DzL4yzHNSfWYujGTYsWGreD000gcgmZ7K4Ys6Tx9TxtsKdDw==", "dev": true }, "@microsoft/signalr": { @@ -15590,9 +15563,9 @@ "requires": {} }, "@ngtools/webpack": { - "version": "15.2.10", - "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-15.2.10.tgz", - "integrity": "sha512-ZExB4rKh/Saad31O/Ofd2XvRuILuCNTYs0+qJL697Be2pzeewvzBhE4Xe1Mm7Jg13aWSPeuIdzSGOqCdwxxxFQ==", + "version": "15.2.11", + "resolved": "https://registry.npmjs.org/@ngtools/webpack/-/webpack-15.2.11.tgz", + "integrity": "sha512-yqp+FziuJ+wIVij4eTqfhuiTPNaG1PU8ukeGOdqkVH4nQMlmzs9UldXy1iYC/6swzn6XO/pkqisU3m/jxemMzA==", "dev": true, "requires": {} }, @@ -15750,9 +15723,9 @@ } }, "@types/body-parser": { - "version": "1.19.3", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.3.tgz", - "integrity": "sha512-oyl4jvAfTGX9Bt6Or4H9ni1Z447/tQuxnZsytsCaExKlmJiU8sFgnIBRzJUpKwB5eWn9HuBYlUlVA74q/yN0eQ==", + "version": "1.19.5", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", + "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", "dev": true, "requires": { "@types/connect": "*", @@ -15760,27 +15733,27 @@ } }, "@types/bonjour": { - "version": "3.5.11", - "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.11.tgz", - "integrity": "sha512-isGhjmBtLIxdHBDl2xGwUzEM8AOyOvWsADWq7rqirdi/ZQoHnLWErHvsThcEzTX8juDRiZtzp2Qkv5bgNh6mAg==", + "version": "3.5.13", + "resolved": "https://registry.npmjs.org/@types/bonjour/-/bonjour-3.5.13.tgz", + "integrity": "sha512-z9fJ5Im06zvUL548KvYNecEVlA7cVDkGUi6kZusb04mpyEFKCIZJvloCcmpmLaIahDpOQGHaHmG6imtPMmPXGQ==", "dev": true, "requires": { "@types/node": "*" } }, "@types/connect": { - "version": "3.4.36", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.36.tgz", - "integrity": "sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==", + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", "dev": true, "requires": { "@types/node": "*" } }, "@types/connect-history-api-fallback": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.1.tgz", - "integrity": "sha512-iaQslNbARe8fctL5Lk+DsmgWOM83lM+7FzP0eQUJs1jd3kBE8NWqBTIT2S8SqQOJjxvt2eyIjpOuYeRXq2AdMw==", + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.5.4.tgz", + "integrity": "sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==", "dev": true, "requires": { "@types/express-serve-static-core": "*", @@ -15819,9 +15792,9 @@ "dev": true }, "@types/express": { - "version": "4.17.19", - "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.19.tgz", - "integrity": "sha512-UtOfBtzN9OvpZPPbnnYunfjM7XCI4jyk1NvnFhTVz5krYAnW4o5DCoIekvms+8ApqhB4+9wSge1kBijdfTSmfg==", + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.21.tgz", + "integrity": "sha512-ejlPM315qwLpaQlQDTjPdsUFSc6ZsP4AN6AlWnogPjQ7CVi7PYF3YVz+CY3jE2pwYf7E/7HlDAN0rV2GxTG0HQ==", "dev": true, "requires": { "@types/body-parser": "*", @@ -15831,9 +15804,9 @@ } }, "@types/express-serve-static-core": { - "version": "4.17.37", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.37.tgz", - "integrity": "sha512-ZohaCYTgGFcOP7u6aJOhY9uIZQgZ2vxC2yWoArY+FeDXlqeH66ZVBjgvg+RLVAS/DWNq4Ap9ZXu1+SUQiiWYMg==", + "version": "4.17.43", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.43.tgz", + "integrity": "sha512-oaYtiBirUOPQGSWNGPWnzyAFJ0BP3cwvN4oWZQY+zUBwpVIGsKUkpBpSztp74drYcjavs7SKFZ4DX1V2QeN8rg==", "dev": true, "requires": { "@types/node": "*", @@ -15849,15 +15822,15 @@ "peer": true }, "@types/http-errors": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.2.tgz", - "integrity": "sha512-lPG6KlZs88gef6aD85z3HNkztpj7w2R7HmR3gygjfXCQmsLloWNARFkMuzKiiY8FGdh1XDpgBdrSf4aKDiA7Kg==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", + "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", "dev": true }, "@types/http-proxy": { - "version": "1.17.12", - "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.12.tgz", - "integrity": "sha512-kQtujO08dVtQ2wXAuSFfk9ASy3sug4+ogFR8Kd8UgP8PEuc1/G/8yjYRmp//PcDNJEUKOza/MrQu15bouEUCiw==", + "version": "1.17.14", + "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.14.tgz", + "integrity": "sha512-SSrD0c1OQzlFX7pGu1eXxSEjemej64aaNPRhhVYUGqXh0BtldAAx37MG8btcumvpgKyZp1F5Gn3JkktdxiFv6w==", "dev": true, "requires": { "@types/node": "*" @@ -15883,29 +15856,38 @@ "version": "4.3.0" }, "@types/mime": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.3.tgz", - "integrity": "sha512-Ys+/St+2VF4+xuY6+kDIXGxbNRO0mesVg0bbxEfB97Od1Vjpjx9KD1qxs64Gcb3CWPirk9Xe+PT4YiiHQ9T+eg==", + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", + "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", "dev": true }, "@types/node": { "version": "14.18.40", "dev": true }, + "@types/node-forge": { + "version": "1.3.11", + "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.11.tgz", + "integrity": "sha512-FQx220y22OKNTqaByeBGqHWYz4cl94tpcxeFdvBo3wjG6XPBuZ0BNgNZRV5J5TFmmcsJ4IzsLkmGRiQbnYsBEQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/parse-json": { "version": "4.0.0", "dev": true }, "@types/qs": { - "version": "6.9.8", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.8.tgz", - "integrity": "sha512-u95svzDlTysU5xecFNTgfFG5RUWu1A9P0VzgpcIiGZA9iraHOdSzcxMxQ55DyeRaGCSxQi7LxXDI4rzq/MYfdg==", + "version": "6.9.14", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.14.tgz", + "integrity": "sha512-5khscbd3SwWMhFqylJBLQ0zIu7c1K6Vz0uBIt915BI3zV0q1nfjRQD3RqSBcPaO6PHEF4ov/t9y89fSiyThlPA==", "dev": true }, "@types/range-parser": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.5.tgz", - "integrity": "sha512-xrO9OoVPqFuYyR/loIHjnbvvyRZREYKLjxV4+dY6v3FQR3stQ9ZxIGkaclF7YhI9hfjpuTbu14hZEy94qKLtOA==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", "dev": true }, "@types/retry": { @@ -15919,9 +15901,9 @@ "dev": true }, "@types/send": { - "version": "0.17.2", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.2.tgz", - "integrity": "sha512-aAG6yRf6r0wQ29bkS+x97BIs64ZLxeE/ARwyS6wrldMm3C1MdKwCcnnEwMC1slI8wuxJOpiUH9MioC0A0i+GJw==", + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", + "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", "dev": true, "requires": { "@types/mime": "^1", @@ -15929,18 +15911,18 @@ } }, "@types/serve-index": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.2.tgz", - "integrity": "sha512-asaEIoc6J+DbBKXtO7p2shWUpKacZOoMBEGBgPG91P8xhO53ohzHWGCs4ScZo5pQMf5ukQzVT9fhX1WzpHihig==", + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/@types/serve-index/-/serve-index-1.9.4.tgz", + "integrity": "sha512-qLpGZ/c2fhSs5gnYsQxtDEq3Oy8SXPClIXkW5ghvAvsNuVSA8k+gCONcUCS/UjLEYvYps+e8uBtfgXgvhwfNug==", "dev": true, "requires": { "@types/express": "*" } }, "@types/serve-static": { - "version": "1.15.3", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.3.tgz", - "integrity": "sha512-yVRvFsEMrv7s0lGhzrggJjNOSmZCdgCjw9xWrPr/kNNLp6FaDfMC1KaYl3TSJ0c58bECwNBMoQrZJ8hA8E1eFg==", + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.5.tgz", + "integrity": "sha512-PDRk21MnK70hja/YF8AHfC7yIsiQHn1rcXx7ijCFBX/k+XQJhQT/gw3xekXKJvx+5SXaMMS8oqQy09Mzvz2TuQ==", "dev": true, "requires": { "@types/http-errors": "*", @@ -15949,9 +15931,9 @@ } }, "@types/sockjs": { - "version": "0.3.34", - "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.34.tgz", - "integrity": "sha512-R+n7qBFnm/6jinlteC9DBL5dGiDGjWAvjo4viUanpnc/dG1y7uDoacXPIQ/PQEg1fI912SMHIa014ZjRpvDw4g==", + "version": "0.3.36", + "resolved": "https://registry.npmjs.org/@types/sockjs/-/sockjs-0.3.36.tgz", + "integrity": "sha512-MK9V6NzAS1+Ud7JV9lJLFqW85VbC9dq3LmwZCuBe4wBDgKC0Kj/jd8Xl+nSviU+Qc3+m7umHHyHg//2KSa0a0Q==", "dev": true, "requires": { "@types/node": "*" @@ -15966,9 +15948,9 @@ "dev": true }, "@types/ws": { - "version": "8.5.7", - "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.7.tgz", - "integrity": "sha512-6UrLjiDUvn40CMrAubXuIVtj2PEfKDffJS7ychvnPU44j+KVeXmdHHTgqcM/dxLUTHxlXHiFM8Skmb8ozGdTnQ==", + "version": "8.5.10", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz", + "integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==", "dev": true, "requires": { "@types/node": "*" @@ -16372,9 +16354,9 @@ } }, "array-flatten": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-2.1.2.tgz", - "integrity": "sha512-hNfzcOV8W4NdualtqBFPyVO+54DSJuZGY9qT4pRroB6S9e3iiido2ISIC5h9R2sPJ8H3FHCIiEnsv1lPXO3KtQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "dev": true }, "array-includes": { @@ -16544,13 +16526,11 @@ } }, "bonjour-service": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.1.1.tgz", - "integrity": "sha512-Z/5lQRMOG9k7W+FkeGTNjh7htqn/2LMnfOvBZ8pynNZCM9MwkQkI3zeI4oz09uWdcgmgHugVvBqxGg4VQJ5PCg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/bonjour-service/-/bonjour-service-1.2.1.tgz", + "integrity": "sha512-oSzCS2zV14bh2kji6vNe7vrpJYCHGvcZnlffFQ1MEoX/WOeQ/teD8SYWKR942OI3INjq8OMNJlbPK5LLLUxFDw==", "dev": true, "requires": { - "array-flatten": "^2.1.2", - "dns-equal": "^1.0.0", "fast-deep-equal": "^3.1.3", "multicast-dns": "^7.2.5" } @@ -16769,7 +16749,9 @@ "dev": true }, "colorette": { - "version": "2.0.19", + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", "dev": true }, "commander": { @@ -17472,12 +17454,6 @@ "path-type": "^4.0.0" } }, - "dns-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", - "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==", - "dev": true - }, "dns-packet": { "version": "5.6.1", "resolved": "https://registry.npmjs.org/dns-packet/-/dns-packet-5.6.1.tgz", @@ -18225,17 +18201,17 @@ } }, "express": { - "version": "4.18.2", - "resolved": "https://registry.npmjs.org/express/-/express-4.18.2.tgz", - "integrity": "sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==", + "version": "4.19.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.19.2.tgz", + "integrity": "sha512-5T6nhjsT+EOMzuck8JjBHARTHfMht0POzlA60WV2pMD3gyXw2LZnZ+ueGdNxG+0calOJcWKbpFcuzLZ91YWq9Q==", "dev": true, "requires": { "accepts": "~1.3.8", "array-flatten": "1.1.1", - "body-parser": "1.20.1", + "body-parser": "1.20.2", "content-disposition": "0.5.4", "content-type": "~1.0.4", - "cookie": "0.5.0", + "cookie": "0.6.0", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", @@ -18263,36 +18239,10 @@ "vary": "~1.1.2" }, "dependencies": { - "array-flatten": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", - "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", - "dev": true - }, - "body-parser": { - "version": "1.20.1", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", - "integrity": "sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==", - "dev": true, - "requires": { - "bytes": "3.1.2", - "content-type": "~1.0.4", - "debug": "2.6.9", - "depd": "2.0.0", - "destroy": "1.2.0", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "on-finished": "2.4.1", - "qs": "6.11.0", - "raw-body": "2.5.1", - "type-is": "~1.6.18", - "unpipe": "1.0.0" - } - }, "cookie": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", - "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==", "dev": true }, "debug": { @@ -18325,18 +18275,6 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, - "raw-body": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz", - "integrity": "sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==", - "dev": true, - "requires": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.4.24", - "unpipe": "1.0.0" - } - }, "statuses": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", @@ -18535,7 +18473,9 @@ } }, "fs-monkey": { - "version": "1.0.3", + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.5.tgz", + "integrity": "sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew==", "dev": true }, "fs.realpath": { @@ -18804,9 +18744,9 @@ } }, "html-entities": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.4.0.tgz", - "integrity": "sha512-igBTJcNNNhvZFRtm8uA6xMY6xYleeDwn3PeBCkDz7tHttv4F2hsDI2aPgNERWzvRcNYHNT3ymRaQzllmXj4YsQ==", + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.5.2.tgz", + "integrity": "sha512-K//PSRMQk4FZ78Kyau+mZurHn3FH0Vwr+H36eE0rPbeYkRRi9YxceYPhuN60UwWorxyKHhqoAJl2OFKa4BVtaA==", "dev": true }, "html-escaper": { @@ -19595,10 +19535,12 @@ } }, "katex": { - "version": "0.16.4", + "version": "0.16.10", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.10.tgz", + "integrity": "sha512-ZiqaC04tp2O5utMsl2TEZTXxa6WSC4yo0fv5ML++D3QZv/vx2Mct0mTlRx3O+uUkjfuAgOkzsCmq5MiUEsDDdA==", "optional": true, "requires": { - "commander": "^8.0.0" + "commander": "^8.3.0" } }, "keycharm": { @@ -19920,10 +19862,12 @@ "dev": true }, "memfs": { - "version": "3.4.13", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz", + "integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==", "dev": true, "requires": { - "fs-monkey": "^1.0.3" + "fs-monkey": "^1.0.4" } }, "merge-descriptors": { @@ -21328,11 +21272,12 @@ "dev": true }, "selfsigned": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.1.1.tgz", - "integrity": "sha512-GSL3aowiF7wa/WtSFwnUrludWFoNhftq8bUkH9pkzjpN2XSPOAYEgg6e0sS9s0rZwgJzJiQRPU18A6clnoW5wQ==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz", + "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==", "dev": true, "requires": { + "@types/node-forge": "^1.3.0", "node-forge": "^1" } }, @@ -22433,7 +22378,9 @@ } }, "webpack-dev-middleware": { - "version": "6.0.1", + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-6.1.2.tgz", + "integrity": "sha512-Wu+EHmX326YPYUpQLKmKbTyZZJIB8/n6R09pTmB03kJmnMsVPTo9COzHZFr01txwaCAuZvfBJE4ZCHRcKs5JaQ==", "dev": true, "requires": { "colorette": "^2.0.10", @@ -22481,9 +22428,9 @@ }, "dependencies": { "webpack-dev-middleware": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.3.tgz", - "integrity": "sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==", + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz", + "integrity": "sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==", "dev": true, "requires": { "colorette": "^2.0.10", @@ -22494,9 +22441,9 @@ } }, "ws": { - "version": "8.14.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.14.2.tgz", - "integrity": "sha512-wEBG1ftX4jcglPxgFCMJmZ2PLtSbJ2Peg6TmpJFTbe9GZYOQCDPdMYu/Tm0/bGZkw8paZnJY45J4K2PZrLYq8g==", + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz", + "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==", "dev": true, "requires": {} } diff --git a/patch-it b/patch-it new file mode 100644 index 00000000..b9a1827d --- /dev/null +++ b/patch-it @@ -0,0 +1,46 @@ +diff --git a/projects/gameboard-ui/src/app/components/gameboard-signalr-hubs/gameboard-signalr-hubs.component.ts b/projects/gameboard-ui/src/app/components/gameboard-signalr-hubs/gameboard-signalr-hubs.component.ts +index 4a62c8e6..90239460 100644 +--- a/projects/gameboard-ui/src/app/components/gameboard-signalr-hubs/gameboard-signalr-hubs.component.ts ++++ b/projects/gameboard-ui/src/app/components/gameboard-signalr-hubs/gameboard-signalr-hubs.component.ts +@@ -73,6 +73,7 @@ export class GameboardSignalRHubsComponent implements OnDestroy { + + // connect to the hubs + await this.gameHub.connect(); ++ await this.supportHub.connect(); + await this.userHub.connect(); + + // listen for interesting events to log +@@ -93,15 +94,10 @@ export class GameboardSignalRHubsComponent implements OnDestroy { + this.log("[GB UserHub]: Hub state is", userHubState); + this.userHubStatusLightState = this.hubStateToStatusLightState(userHubState); + this.userHubTooltip = `UserHub: ${userHubState}`; +- }) +- ); ++ }), + +- // join the support hub (which everyone uses to get ticket updates) +- if (u.isAdmin || u.isSupport) { +- await this.supportHub.connect(); +- await this.supportHub.joinStaffGroup(); +- this.unsub.add(this.supportHub.hubState$.subscribe(supportHubState => this.supportHubStatusLightState = this.hubStateToStatusLightState(supportHubState))); +- } ++ this.supportHub.hubState$.subscribe(supportHubState => this.supportHubStatusLightState = this.hubStateToStatusLightState(supportHubState)) ++ ); + } + } + +diff --git a/projects/gameboard-ui/src/app/services/signalR/support-hub.service.ts b/projects/gameboard-ui/src/app/services/signalR/support-hub.service.ts +index 67ba3dcb..c05474ae 100644 +--- a/projects/gameboard-ui/src/app/services/signalR/support-hub.service.ts ++++ b/projects/gameboard-ui/src/app/services/signalR/support-hub.service.ts +@@ -41,10 +41,6 @@ export class SupportHubService { + ); + } + +- public async joinStaffGroup() { +- await this._signalRService.sendMessage("joinStaffGroup"); +- } +- + private handleTicketClosed(ev: SupportHubEvent) { + this.logService.logInfo("[GB Support Hub Staff Group]: Ticket Closed", ev); + diff --git a/projects/gameboard-ui/src/app/admin/admin-player-session/admin-player-session.component.html b/projects/gameboard-ui/src/app/admin/admin-player-session/admin-player-session.component.html index 85ccbbb1..cc576872 100644 --- a/projects/gameboard-ui/src/app/admin/admin-player-session/admin-player-session.component.html +++ b/projects/gameboard-ui/src/app/admin/admin-player-session/admin-player-session.component.html @@ -79,11 +79,16 @@

Other tools

Unenroll + (confirm)="onResetSessionRequest.emit({ player, resetType: 'preserveChallenges' })"> + Reset Session (Preserve Challenges) + + Reset Session Reset Session & + (confirm)="onResetSessionRequest.emit({ player, resetType: 'unenrollAndArchiveChallenges' })">Reset Session + & Unenroll diff --git a/projects/gameboard-ui/src/app/admin/admin.module.ts b/projects/gameboard-ui/src/app/admin/admin.module.ts index 01c21ed5..a5d504b4 100644 --- a/projects/gameboard-ui/src/app/admin/admin.module.ts +++ b/projects/gameboard-ui/src/app/admin/admin.module.ts @@ -48,6 +48,7 @@ import { SiteOverviewStatsComponent } from './components/site-overview-stats/sit import { SpecBrowserComponent } from './spec-browser/spec-browser.component'; import { SponsorBrowserComponent } from './sponsor-browser/sponsor-browser.component'; import { SupportReportLegacyComponent } from './support-report-legacy/support-report-legacy.component'; +import { SyncStartTeamPlayerReadyCountPipe } from './pipes/sync-start-team-player-ready-count.pipe'; import { SystemNotificationsModule } from '@/system-notifications/system-notifications.module'; import { TeamAdminContextMenuComponent } from './components/team-admin-context-menu/team-admin-context-menu.component'; import { TeamObserverComponent } from './team-observer/team-observer.component'; @@ -62,6 +63,7 @@ import { ExtendTeamsModalComponent } from './components/extend-teams-modal/exten import { ActiveTeamsModalComponent } from './components/active-teams-modal/active-teams-modal.component'; import { AdminEnrollTeamModalComponent } from './components/admin-enroll-team-modal/admin-enroll-team-modal.component'; import { GameYamlImportModalComponent } from './components/game-yaml-import-modal/game-yaml-import-modal.component'; +import { SyncStartGameStateDescriptionPipe } from './pipes/sync-start-game-state-description.pipe'; @NgModule({ declarations: [ @@ -113,6 +115,8 @@ import { GameYamlImportModalComponent } from './components/game-yaml-import-moda ActiveTeamsModalComponent, AdminEnrollTeamModalComponent, GameYamlImportModalComponent, + SyncStartTeamPlayerReadyCountPipe, + SyncStartGameStateDescriptionPipe, ], imports: [ CommonModule, diff --git a/projects/gameboard-ui/src/app/admin/components/active-challenges-modal/active-challenges-modal.component.html b/projects/gameboard-ui/src/app/admin/components/active-challenges-modal/active-challenges-modal.component.html index 38f5d123..3aa8f673 100644 --- a/projects/gameboard-ui/src/app/admin/components/active-challenges-modal/active-challenges-modal.component.html +++ b/projects/gameboard-ui/src/app/admin/components/active-challenges-modal/active-challenges-modal.component.html @@ -9,69 +9,94 @@ + + + + @@ -84,6 +109,10 @@
There aren't any active challenges of this type right now.
+ +
None of the active challenges match your search.
+
+ diff --git a/projects/gameboard-ui/src/app/admin/components/active-challenges-modal/active-challenges-modal.component.ts b/projects/gameboard-ui/src/app/admin/components/active-challenges-modal/active-challenges-modal.component.ts index 54f1f6b1..4dc4bd9e 100644 --- a/projects/gameboard-ui/src/app/admin/components/active-challenges-modal/active-challenges-modal.component.ts +++ b/projects/gameboard-ui/src/app/admin/components/active-challenges-modal/active-challenges-modal.component.ts @@ -5,7 +5,6 @@ import { PlayerMode } from '@/api/player-models'; import { AppActiveChallengeSpec } from '@/api/admin.models'; import { AdminService } from '@/api/admin.service'; import { ModalConfirmService } from '@/services/modal-confirm.service'; -import { RouterService } from '@/services/router.service'; @Component({ selector: 'app-active-challenges-modal', @@ -19,12 +18,13 @@ export class ActiveChallengesModalComponent implements OnInit { protected fa = fa; protected isPracticeMode = false; protected isWorking = false; + + protected matchingSpecs: AppActiveChallengeSpec[] = []; protected specs: AppActiveChallengeSpec[] = []; constructor( private adminService: AdminService, - private modalService: ModalConfirmService, - private routerService: RouterService) { } + private modalService: ModalConfirmService) { } async ngOnInit(): Promise { if (!this.playerMode) @@ -33,6 +33,22 @@ export class ActiveChallengesModalComponent implements OnInit { await this.load(this.playerMode); } + protected handleSearchInput(text: string) { + if (!text) { + this.matchingSpecs = this.specs; + return; + } + + text = text.toLowerCase(); + + this.matchingSpecs = this.specs.filter(s => + s.id.toLowerCase().indexOf(text) >= 0 || + s.name.toLowerCase().indexOf(text) >= 0 || + s.challenges.some(c => c.team.id.indexOf(text) >= 0) || + s.challenges.some(c => c.id.indexOf(text) >= 0) + ); + } + protected close() { this.modalService.hide(); } @@ -44,6 +60,7 @@ export class ActiveChallengesModalComponent implements OnInit { try { this.isWorking = true; const response = await firstValueFrom(this.adminService.getActiveChallenges(playerMode)); + this.matchingSpecs = response.specs; this.specs = response.specs; this.isWorking = false; } diff --git a/projects/gameboard-ui/src/app/admin/components/active-teams-modal/active-teams-modal.component.html b/projects/gameboard-ui/src/app/admin/components/active-teams-modal/active-teams-modal.component.html index 443b6811..f0b15f36 100644 --- a/projects/gameboard-ui/src/app/admin/components/active-teams-modal/active-teams-modal.component.html +++ b/projects/gameboard-ui/src/app/admin/components/active-teams-modal/active-teams-modal.component.html @@ -9,9 +9,21 @@ -
+ +
- + + + + +
+ +
+ + + +
diff --git a/projects/gameboard-ui/src/app/admin/components/challenge-spec-editor/challenge-spec-editor.component.ts b/projects/gameboard-ui/src/app/admin/components/challenge-spec-editor/challenge-spec-editor.component.ts index 05eea5e6..ef6acbec 100644 --- a/projects/gameboard-ui/src/app/admin/components/challenge-spec-editor/challenge-spec-editor.component.ts +++ b/projects/gameboard-ui/src/app/admin/components/challenge-spec-editor/challenge-spec-editor.component.ts @@ -1,11 +1,11 @@ +import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core'; +import { Subject, debounceTime, filter, switchMap, tap } from 'rxjs'; import { Spec } from '@/api/spec-models'; import { SpecService } from '@/api/spec.service'; import { fa } from '@/services/font-awesome.service'; import { ChallengeSpecScoringConfig } from '@/services/scoring/scoring.models'; import { UnsubscriberService } from '@/services/unsubscriber.service'; import { slug } from '@/tools/functions'; -import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core'; -import { Subject, debounceTime, filter, switchMap, tap } from 'rxjs'; @Component({ selector: 'app-challenge-spec-editor', diff --git a/projects/gameboard-ui/src/app/admin/components/external-game-admin-player-context-menu/external-game-admin-player-context-menu.component.html b/projects/gameboard-ui/src/app/admin/components/external-game-admin-player-context-menu/external-game-admin-player-context-menu.component.html index e3cc29f0..f36de701 100644 --- a/projects/gameboard-ui/src/app/admin/components/external-game-admin-player-context-menu/external-game-admin-player-context-menu.component.html +++ b/projects/gameboard-ui/src/app/admin/components/external-game-admin-player-context-menu/external-game-admin-player-context-menu.component.html @@ -6,8 +6,7 @@
  • After starting the session, the clock expires after {{ctx.game.sessionMinutes}} minutes!
  • -
  • - This will start the session for the team, so be sure your - teammates are ready to start! +
  • + This will start the session for the team, so be sure your teammates are ready to start!
  • You cannot add or remove team members after your session has started!
  • -
  • You will not be able to reset the session after starting it!
  • +
  • You will not be able to reset the session after starting it.
@@ -85,6 +91,10 @@

Are you ready?

+ + + + - Connecting to the game... + diff --git a/projects/gameboard-ui/src/app/game/components/session-start-controls/session-start-controls.component.ts b/projects/gameboard-ui/src/app/game/components/session-start-controls/session-start-controls.component.ts index 1765b9ee..4ce1a7cc 100644 --- a/projects/gameboard-ui/src/app/game/components/session-start-controls/session-start-controls.component.ts +++ b/projects/gameboard-ui/src/app/game/components/session-start-controls/session-start-controls.component.ts @@ -9,6 +9,7 @@ import { SyncStartGameState } from '@/game/game.models'; import { GameHubService } from '@/services/signalR/game-hub.service'; import { LogService } from '@/services/log.service'; import { UnsubscriberService } from '@/services/unsubscriber.service'; +import { HubConnectionState } from '@microsoft/signalr'; @Component({ selector: 'app-session-start-controls', @@ -26,6 +27,7 @@ export class SessionStartControlsComponent implements OnInit { protected isDoubleChecking = false; protected isGameSyncStartReady = false; protected isConnectedToGameHub = false; + protected isHubConnectionError = false; protected isReadyingUp = false; protected playerReadyCount = 0; protected playerNotReadyCount = 0; @@ -43,17 +45,21 @@ export class SessionStartControlsComponent implements OnInit { if (this.ctx.game.requireSynchronizedStart) { // update stuff state of the game hub on init this.handleNewSyncStartState(await firstValueFrom(this.gameService.getSyncStartState(this.ctx.game.id))); - this.isConnectedToGameHub = this.gameHub.isConnectedToGame(this.ctx.game.id); // when the hub updates, maintain state this.unsub.add( - this.gameHub.joinedGameIds$.subscribe(gameIds => { - this.isConnectedToGameHub = gameIds.some(gId => gId == this.ctx.game.id); + this.gameHub.activeEnrollments$.subscribe(() => this.isConnectedToGameHub = this.gameHub.isConnectedToGame(this.ctx.game.id)), + this.gameHub.hubState$.subscribe(state => { + this.isConnectedToGameHub = this.gameHub.isConnectedToGame(this.ctx.game.id); + this.isHubConnectionError = (state === HubConnectionState.Disconnected || state === HubConnectionState.Disconnecting); }), + this.gameHub.syncStartGameStateChanged$.subscribe(stateUpdate => { - this.logService.logInfo("State update", stateUpdate); - this.handleNewSyncStartState(stateUpdate); - }) + if (stateUpdate.game.id === this.ctx.game.id) { + this.logService.logInfo("State update", stateUpdate); + this.handleNewSyncStartState(stateUpdate); + } + }), ); } } diff --git a/projects/gameboard-ui/src/app/game/game.module.ts b/projects/gameboard-ui/src/app/game/game.module.ts index c4af9ab2..aa738c65 100644 --- a/projects/gameboard-ui/src/app/game/game.module.ts +++ b/projects/gameboard-ui/src/app/game/game.module.ts @@ -26,7 +26,7 @@ import { GamePageComponent } from './pages/game-page/game-page.component'; import { GamespaceQuizComponent } from './gamespace-quiz/gamespace-quiz.component'; import { HubStateToPlayerStatusPipe } from './pipes/hub-state-to-player-status.pipe'; import { IndexToSubmittedAnswersPipe } from './pipes/index-to-submitted-answers.pipe'; -import { ManualBonusesToTooltipPipe } from './pipes/manual-bonuses-to-tooltip.pipe'; +import { ChallengeBonusesToTooltip } from './pipes/manual-bonuses-to-tooltip.pipe'; import { PlayComponent } from './components/play/play.component'; import { PlayerEnrollComponent } from './player-enroll/player-enroll.component'; import { PlayerPresenceComponent } from './player-presence/player-presence.component'; @@ -57,7 +57,7 @@ const MODULE_DECLARATIONS = [ HubStateToPlayerStatusPipe, IndexToSubmittedAnswersPipe, LateStartBannerComponent, - ManualBonusesToTooltipPipe, + ChallengeBonusesToTooltip, PlayComponent, PlayerEnrollComponent, PlayerPresenceComponent, diff --git a/projects/gameboard-ui/src/app/game/pages/external-game-loading-page/external-game-loading-page.component.ts b/projects/gameboard-ui/src/app/game/pages/external-game-loading-page/external-game-loading-page.component.ts index 13f30cef..9710c118 100644 --- a/projects/gameboard-ui/src/app/game/pages/external-game-loading-page/external-game-loading-page.component.ts +++ b/projects/gameboard-ui/src/app/game/pages/external-game-loading-page/external-game-loading-page.component.ts @@ -103,13 +103,12 @@ export class ExternalGameLoadingPageComponent implements OnInit { } this.log.logInfo("Checking hub connection to game", ctx.game.id); - if (!this.gameHub.isConnectedToGame(ctx.game.id)) { - this.log.logInfo(`Not connected to game "${ctx.game.id}" - connecting now.`); - await this.gameHub.joinGame(ctx.game.id); - this.log.logInfo(`Connected to game "${ctx.game.id}".`); - } - else { - this.log.logInfo(`Already connected to game "${ctx.game.id}". Skipping connection.`); + const isConnected = this.gameHub.isConnectedToGame(ctx.game.id); + + if (!isConnected) { + const error = `Not connected to hub for game ${ctx.game.id}`; + this.errors.push(error); + throw new Error(error); } if (ctx.game.mode == GameEngineMode.External) { diff --git a/projects/gameboard-ui/src/app/game/pages/external-game-page/external-game-page.component.html b/projects/gameboard-ui/src/app/game/pages/external-game-page/external-game-page.component.html index 9458a484..0e5f081d 100644 --- a/projects/gameboard-ui/src/app/game/pages/external-game-page/external-game-page.component.html +++ b/projects/gameboard-ui/src/app/game/pages/external-game-page/external-game-page.component.html @@ -11,7 +11,6 @@

Connecting to external game at

{{ game.externalGameClientUrl }}

- diff --git a/projects/gameboard-ui/src/app/game/pages/game-page/game-page.component.ts b/projects/gameboard-ui/src/app/game/pages/game-page/game-page.component.ts index 3e880a00..8e480352 100644 --- a/projects/gameboard-ui/src/app/game/pages/game-page/game-page.component.ts +++ b/projects/gameboard-ui/src/app/game/pages/game-page/game-page.component.ts @@ -50,6 +50,7 @@ export class GamePageComponent implements OnDestroy { private needsReloadOnUnenroll = false; private hubEventsSubcription: Subscription; private localUserSubscription: Subscription; + private externalGameLaunchStartedSubscription?: Subscription; constructor( apiGame: GameService, @@ -66,7 +67,6 @@ export class GamePageComponent implements OnDestroy { private modalService: ModalConfirmService, private routerService: RouterService, private titleService: AppTitleService, - private unsub: UnsubscriberService, private windowService: WindowService ) { const user$ = localUser.user$.pipe(map(u => !!u ? u : {} as ApiUser)); @@ -148,7 +148,6 @@ export class GamePageComponent implements OnDestroy { } if (playerEvent.hubEvent.action == HubEventAction.deleted) { - this.gameHubService.leaveGame(this.ctxIds.gameId); this.showModal(playerEvent.hubEvent.actingUser?.name); this.player$.next(null); } @@ -191,10 +190,13 @@ export class GamePageComponent implements OnDestroy { // join the hub that coordinates across everyone playing this game // (for sync start, external game launch, etc.) if (ctx.game.requireSynchronizedStart || this.isExternalGame) { - this.logService.logInfo("This is an external or sync-start game. Joining the game hub..."); - await this.joinGameHub({ - game: ctx.game, - player: ctx.player + this.logService.logInfo("This is an external or sync-start game. Listening for game hub events..."); + + // wire up listeners to move the player along when sync start is ready + this.externalGameLaunchStartedSubscription?.unsubscribe(); + this.externalGameLaunchStartedSubscription = this.gameHubService.externalGameLaunchStarted$.subscribe(startState => { + if (startState) + this.redirectToExternalGameLoadingPage(ctx); }); } }) @@ -204,9 +206,7 @@ export class GamePageComponent implements OnDestroy { ngOnDestroy(): void { this.hubEventsSubcription?.unsubscribe(); this.localUserSubscription?.unsubscribe(); - - if (this.ctxIds.gameId) - this.gameHubService.leaveGame(this.ctxIds.gameId); + this.externalGameLaunchStartedSubscription?.unsubscribe(); } protected onSessionStarted(player: Player): void { @@ -225,22 +225,7 @@ export class GamePageComponent implements OnDestroy { this.resetEnrollmentAndLeaveGame(player); } - private async joinGameHub(ctx: GameEnrollmentContext) { - // note that this should (currently) only happen if the game is BOTH sync start AND external - await this.gameHubService.joinGame(ctx.game.id); - - // wire up listeners to move the player along when sync start is ready - this.unsub.add( - this.gameHubService.externalGameLaunchStarted$.subscribe(startState => { - if (startState) { - this.redirectToExternalGameLoadingPage(ctx); - } - }), - ); - } - private async resetEnrollmentAndLeaveGame(player: Player) { - await this.gameHubService.leaveGame(this.ctxIds.gameId); await this.hub.disconnect(); this.player$.next(null); diff --git a/projects/gameboard-ui/src/app/game/pipes/manual-bonuses-to-tooltip.pipe.ts b/projects/gameboard-ui/src/app/game/pipes/manual-bonuses-to-tooltip.pipe.ts index c79e5586..8ffea638 100644 --- a/projects/gameboard-ui/src/app/game/pipes/manual-bonuses-to-tooltip.pipe.ts +++ b/projects/gameboard-ui/src/app/game/pipes/manual-bonuses-to-tooltip.pipe.ts @@ -1,7 +1,7 @@ import { Pipe, PipeTransform } from '@angular/core'; -@Pipe({ name: 'manualBonusesToTooltip' }) -export class ManualBonusesToTooltipPipe implements PipeTransform { +@Pipe({ name: 'challengeBonusesToTooltip' }) +export class ChallengeBonusesToTooltip implements PipeTransform { transform(value: { description: string }[]): string { if (!value || !value.length) diff --git a/projects/gameboard-ui/src/app/game/player-session/player-session.component.ts b/projects/gameboard-ui/src/app/game/player-session/player-session.component.ts index 67dd6b22..6798c5eb 100644 --- a/projects/gameboard-ui/src/app/game/player-session/player-session.component.ts +++ b/projects/gameboard-ui/src/app/game/player-session/player-session.component.ts @@ -164,7 +164,7 @@ export class PlayerSessionComponent implements OnDestroy { this.errors = []; this.isResetting = true; - await firstValueFrom(this.teamService.resetSession(p.teamId, { unenrollTeam: true })); + await firstValueFrom(this.teamService.resetSession({ teamId: p.teamId, resetType: "unenrollAndArchiveChallenges" })); delete p.session; this.onSessionReset.emit(p); this.player$.next(p); diff --git a/projects/gameboard-ui/src/app/services/signalR/game-hub.models.ts b/projects/gameboard-ui/src/app/services/signalR/game-hub.models.ts index e954e7a0..781f2d0f 100644 --- a/projects/gameboard-ui/src/app/services/signalR/game-hub.models.ts +++ b/projects/gameboard-ui/src/app/services/signalR/game-hub.models.ts @@ -18,25 +18,24 @@ export class GameHubEventType implements SignalRHubEventType { static ExternalGameLaunchStart = "externalGameLaunchStart"; static ExternalGameLaunchEnd = "externalGameLaunchEnd"; static ExternalGameLaunchFailure = "externalGameLaunchFailure"; - static PlayerJoined = "playerJoined"; static SyncStartGameStarted = "syncStartGameStarted"; static SyncStartGameStarting = "syncStartGameStarting"; static SyncStartGameStateChanged = "syncStartGameStateChanged"; static VerifyAllPlayersConnectedStart = "verifyAllPlayersConnectedStart"; static VerifyAllPlayersConnectedCountChange = "verifyAllPlayersConnectedCountChange"; static VerifyAllPlayersConnectedEnd = "verifyAllPlayersConnectedEnd"; - static YouJoined = "YouJoined"; + // static YourActiveGamesChanged = "YourActiveGamesChanged"; } -export interface PlayerJoinedEvent { - gameId: string; +export interface GameHubActiveEnrollment { + game: SimpleEntity; player: SimpleEntity; } -export interface YouJoinedEvent { - connectionId: string; - userId: string; -} +// export interface YourActiveGamesChanged { +// userId: string; +// activeEnrollments: GameHubActiveEnrollment[]; +// } export interface GameStartState { game: SimpleEntity; diff --git a/projects/gameboard-ui/src/app/services/signalR/game-hub.service.ts b/projects/gameboard-ui/src/app/services/signalR/game-hub.service.ts index 3c1248be..03e83995 100644 --- a/projects/gameboard-ui/src/app/services/signalR/game-hub.service.ts +++ b/projects/gameboard-ui/src/app/services/signalR/game-hub.service.ts @@ -1,36 +1,37 @@ import { Injectable } from '@angular/core'; import { HubConnectionState } from '@microsoft/signalr'; import { SignalRService } from './signalr.service'; -import { GameStartState, GameHubEvent, GameHubEventType, PlayerJoinedEvent } from './game-hub.models'; +import { GameStartState, GameHubEvent, GameHubEventType, GameHubActiveEnrollment } from './game-hub.models'; import { LogService } from '@/services/log.service'; -import { BehaviorSubject, Observable, Subject, tap } from 'rxjs'; +import { BehaviorSubject, Observable, Subject } from 'rxjs'; import { SyncStartGameState, SyncStartGameStartedState } from '@/game/game.models'; import { ConfigService } from '@/utility/config.service'; import { UserService } from '@/api/user.service'; @Injectable({ providedIn: 'root' }) export class GameHubService { + private _signalRService: SignalRService; + + private _activeEnrollments$ = new BehaviorSubject([]); private _externalGameLaunchStarted$ = new Subject(); private _externalGameLaunchEnded$ = new Subject(); private _externalGameLaunchFailure$ = new Subject(); private _externalGameLaunchProgressChanged$ = new Subject(); - private _gameJoined$ = new Subject(); - private _joinedGameIds$ = new BehaviorSubject([]); - private _signalRService: SignalRService; private _syncStartGameStateChanged$ = new Subject(); private _syncStartGameStarted$ = new Subject(); private _syncStartGameStarting$ = new Subject(); + // private _yourActiveGamesChanged$ = new Subject(); + public activeEnrollments$ = this._activeEnrollments$.asObservable(); public externalGameLaunchProgressChanged$ = this._externalGameLaunchProgressChanged$.asObservable(); public externalGameLaunchStarted$ = this._externalGameLaunchStarted$.asObservable(); public externalGameLaunchEnded$ = this._externalGameLaunchEnded$.asObservable(); public externalGameLaunchFailure$ = this._externalGameLaunchFailure$.asObservable(); - public gameJoined$ = this._gameJoined$.asObservable(); public hubState$: Observable; - public joinedGameIds$ = this._joinedGameIds$.asObservable(); public syncStartGameStateChanged$ = this._syncStartGameStateChanged$.asObservable(); public syncStartGameStarted$ = this._syncStartGameStarted$.asObservable(); public syncStartGameStarting$ = this._syncStartGameStarting$.asObservable(); + // public yourActiveGamesChanged$ = this._yourActiveGamesChanged$.asObservable(); constructor( configService: ConfigService, @@ -44,37 +45,12 @@ export class GameHubService { await this._signalRService.disconnect(); } - async joinGame(gameId: string) { - if (!gameId) { - this.logService.logError("Can't join game with falsey id", gameId); - } - - if (this._joinedGameIds$.value.some(gId => gId == gameId)) { - this.logService.logWarning(`Tried to join ${gameId} but was already connected.`); - return; - } - - this.logService.logInfo(`Game hub is joining game ${gameId}...`); - this._signalRService.sendMessageWithArg("JoinGame", { gameId }); - this._joinedGameIds$.next([...this._joinedGameIds$.value, gameId]); - } - - async leaveGame(gameId: string) { - if (!this._joinedGameIds$.value.some(gId => gId == gameId)) { - this.logService.logInfo(`Attempted to leave gameId ${gameId}, but this player is not in the game's group.`); - return; - } - - await this._signalRService.sendMessageWithArg("LeaveGame", { gameId }); - this._joinedGameIds$.next(this._joinedGameIds$.value.filter(g => g !== gameId)); - } - - isConnected() { - return this._signalRService.isConnnected(); - } - isConnectedToGame = (gameId: string) => { - return this._signalRService.isConnnected() && this._joinedGameIds$.value.some(gId => gId === gameId); + // return this._signalRService.connectionState == HubConnectionState.Connected && this._activeEnrollments$.value.some(enrollment => + // enrollment.game.id === gameId + // ); + + return this._signalRService.connectionState == HubConnectionState.Connected; }; // This should _only_ be called once per login by the @@ -83,7 +59,6 @@ export class GameHubService { await this._signalRService.connect( "hub/games", [ - { eventType: GameHubEventType.PlayerJoined, handler: this.handleGameJoined.bind(this) }, { eventType: GameHubEventType.ExternalGameChallengesDeployStart, handler: this.handleExternalGameLaunchProgressChanged.bind(this) }, { eventType: GameHubEventType.ExternalGameChallengesDeployProgressChange, handler: this.handleExternalGameLaunchProgressChanged.bind(this) }, { eventType: GameHubEventType.ExternalGameChallengesDeployEnd, handler: this.handleExternalGameLaunchProgressChanged.bind(this) }, @@ -96,15 +71,10 @@ export class GameHubService { { eventType: GameHubEventType.SyncStartGameStateChanged, handler: this.handleSyncStartStateChanged.bind(this) }, { eventType: GameHubEventType.SyncStartGameStarted, handler: this.handleSyncStartGameStarted.bind(this) }, { eventType: GameHubEventType.SyncStartGameStarting, handler: this.handleSyncStartGameStarting.bind(this) }, - { eventType: GameHubEventType.YouJoined, handler: ev => this.handleGameJoined.bind(this) } + // { eventType: GameHubEventType.YourActiveGamesChanged, handler: this.handleYourActiveGamesChanged.bind(this) } ]); } - private handleGameJoined(ev: GameHubEvent) { - this.logService.logInfo("Joined the game", ev); - this._gameJoined$.next(ev.data); - } - private handleExternalGameLaunchStarted(ev: GameHubEvent) { this._externalGameLaunchStarted$.next(ev.data); } @@ -133,4 +103,9 @@ export class GameHubService { private handleSyncStartGameStarting(ev: GameHubEvent) { this.logService.logInfo("Sync start game starting...", ev.data); } + + // private handleYourActiveGamesChanged(ev: GameHubEvent) { + // this.logService.logInfo("You joined the game hub!", ev.data); + // this._activeEnrollments$.next(ev.data.activeEnrollments.sort((a, b) => a.game.name.localeCompare(b.game.name))); + // } } diff --git a/projects/gameboard-ui/src/app/services/signalR/signalr.service.ts b/projects/gameboard-ui/src/app/services/signalR/signalr.service.ts index 8e027f88..ea62b400 100644 --- a/projects/gameboard-ui/src/app/services/signalR/signalr.service.ts +++ b/projects/gameboard-ui/src/app/services/signalR/signalr.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@angular/core'; -import { BehaviorSubject, Subject, firstValueFrom, map, timer } from 'rxjs'; +import { BehaviorSubject, firstValueFrom, map, timer } from 'rxjs'; import { HttpTransportType, HubConnection, HubConnectionBuilder, HubConnectionState, IHttpConnectionOptions } from '@microsoft/signalr'; import { UserService } from '@/api/user.service'; import { ConfigService } from '@/utility/config.service'; @@ -13,10 +13,12 @@ export class SignalRService { private readonly AUTO_RECONNECT_INTERVALS = [1000, 2000, 3000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000]; private _connection?: HubConnection; + private _connectionId$ = new BehaviorSubject(null); private _connectionState$ = new BehaviorSubject(HubConnectionState.Disconnected); private _events$ = new BehaviorSubject | null>(null); private _eventHandlers: SignalRHubEventHandlerCollection = {}; + public connectionId$ = this._connectionId$.asObservable(); public events$ = this._events$.asObservable(); public state$ = this._connectionState$.asObservable(); @@ -43,13 +45,14 @@ export class SignalRService { await this.disconnect(); } - this.logger.logInfo(this.formatForlog(`Building connection to`, connectToUrl)); + this.logger.logInfo(this.formatForlog("Building connection to", connectToUrl)); this._connection = this.buildConnection(connectToUrl, eventHandlers); - this.logger.logInfo(this.formatForlog(`Starting connection to`, connectToUrl)); + + this.logger.logInfo(this.formatForlog("Starting connection to", connectToUrl)); await this.resolveOpenConnection(this._connection); - // this can resolve without an actual connection id, which is wild to me - // have to check the connection state rather than id to confirm + // because we skip negotiation below (which has unclear benefit to us but we're not messing with it for now) + // this can/does resolve without a connection id, so check the state instead if (this._connection?.state == HubConnectionState.Connected) { this.logger.logInfo(`Connection started with url`, connectToUrl); } @@ -59,15 +62,14 @@ export class SignalRService { else { this.logger.logError(`CRITICAL: The SignalR service was unable to join hub "${connectToUrl}".`); } - } - public isConnnected() { - return this.isConnectedOrConnectingState(this._connectionState$.value); + this.updateState(); } public async disconnect(): Promise { this.logger.logInfo("Disconnecting from hub at ", this._connection?.baseUrl); this._connection?.stop(); + this.updateState(); this.logger.logInfo("Disconnected from", this._connection?.baseUrl); } @@ -82,6 +84,7 @@ export class SignalRService { } if (this._connection.state !== HubConnectionState.Connected) { + this.updateState(); this.logger.logWarning(`Can't send message "${message}" with parameters ${arg}. The hub is not connected (State: "${this._connection?.state}"). Attempting to connect...`); await this.resolveOpenConnection(this._connection); this.logger.logWarning(`Connected. Now sending message: "${message}."`); @@ -104,7 +107,8 @@ export class SignalRService { authResult = firstValueFrom(this.userService.ticket().pipe(map(r => r?.ticket))); } catch (err: any) { - this._connectionState$.next(this._connection?.state || HubConnectionState.Disconnected); + this.logger.logError("[GB SignalR Error]:", err); + this.updateState(); } return authResult; @@ -119,6 +123,7 @@ export class SignalRService { connection.onclose(err => this.handleClose.bind(this)(err)); connection.onreconnected(cid => this.handleConnected.bind(this)(cid)); + connection.onreconnecting(err => this.handleReconnecting.bind(this)(err)); this.bindEventHandlersToConnection(connection, eventHandlers, this._eventHandlers); return connection; @@ -145,12 +150,13 @@ export class SignalRService { private async resolveOpenConnection(connection: HubConnection, attemptIntervals: number[] = this.AUTO_RECONNECT_INTERVALS): Promise { if (connection.state == HubConnectionState.Disconnected) { + this.logger.logInfo("Disconnected - connecting to the SignalR hub at", connection.baseUrl); await connection.start(); } if (connection.state === HubConnectionState.Connected) { - this.logger.logInfo("Connected to SignalR hub at", connection.baseUrl); - this.handleConnected(); + this.logger.logInfo("Already connected to SignalR hub at", connection.baseUrl); + this.handleConnected(connection.connectionId || undefined); return; } @@ -166,12 +172,14 @@ export class SignalRService { private handleConnected(connectionId?: string) { this.logger.logInfo(`SignalR Service | Connected ${(connectionId ? `connection id "${connectionId}"` : "")}`); + this._connectionId$.next(connectionId || null); this.updateState(); } private handleClose(err?: Error) { this.logger.logInfo(this.formatForlog("SignalR service connection closed.")); this._eventHandlers = {}; + this._connectionId$.next(null); if (err) { this.logger.logError(this.formatForlog(`SignalR Service Error: ${err}`)); @@ -180,6 +188,11 @@ export class SignalRService { this.updateState(); } + private handleReconnecting(err?: Error) { + this.logger.logInfo(this.formatForlog("SignalR service is reconnecting..."), err); + this.updateState(); + } + private formatForlog(...params: any[]): any[] { return ["[GB SignalRService]:", ...params]; } diff --git a/projects/gameboard-ui/src/app/services/signalR/support-hub.service.ts b/projects/gameboard-ui/src/app/services/signalR/support-hub.service.ts index 67ba3dcb..c05474ae 100644 --- a/projects/gameboard-ui/src/app/services/signalR/support-hub.service.ts +++ b/projects/gameboard-ui/src/app/services/signalR/support-hub.service.ts @@ -41,10 +41,6 @@ export class SupportHubService { ); } - public async joinStaffGroup() { - await this._signalRService.sendMessage("joinStaffGroup"); - } - private handleTicketClosed(ev: SupportHubEvent) { this.logService.logInfo("[GB Support Hub Staff Group]: Ticket Closed", ev); diff --git a/projects/gameboard-ui/src/app/sponsors/components/sponsor-with-children-picker/sponsor-with-children-picker.component.ts b/projects/gameboard-ui/src/app/sponsors/components/sponsor-with-children-picker/sponsor-with-children-picker.component.ts index f9218275..10c65fe8 100644 --- a/projects/gameboard-ui/src/app/sponsors/components/sponsor-with-children-picker/sponsor-with-children-picker.component.ts +++ b/projects/gameboard-ui/src/app/sponsors/components/sponsor-with-children-picker/sponsor-with-children-picker.component.ts @@ -1,4 +1,5 @@ import { Sponsor } from '@/api/sponsor-models'; +import { SponsorService } from '@/api/sponsor.service'; import { ApiUser } from '@/api/user-models'; import { UserService } from '@/api/user.service'; import { ToastService } from '@/utility/services/toast.service'; @@ -21,6 +22,7 @@ export class SponsorWithChildrenPickerComponent implements OnInit { constructor( private api: UserService, private localUserService: LocalUserService, + private sponsorService: SponsorService, private toastService: ToastService) { this.localUser$ = localUserService.user$.asObservable(); } @@ -37,7 +39,10 @@ export class SponsorWithChildrenPickerComponent implements OnInit { sponsorId: sponsor.id })); - this.toastService.showMessage(`Your sponsor has been changed to "${sponsor.name}".`) + this.toastService.show({ + avatarUrl: this.sponsorService.resolveAbsoluteSponsorLogoUri(sponsor.logo), + text: `Your sponsor has been changed to **${sponsor.name}**.` + }); this.localUserService.refresh(); } } diff --git a/projects/gameboard-ui/src/app/system-notifications/components/admin-system-notifications/admin-system-notifications.component.html b/projects/gameboard-ui/src/app/system-notifications/components/admin-system-notifications/admin-system-notifications.component.html index e7621ff9..00bda048 100644 --- a/projects/gameboard-ui/src/app/system-notifications/components/admin-system-notifications/admin-system-notifications.component.html +++ b/projects/gameboard-ui/src/app/system-notifications/components/admin-system-notifications/admin-system-notifications.component.html @@ -7,7 +7,7 @@

Notifications

optionally configure below). Once a player dismisses a notification, they won't see it again.

-
+
- +
Availability dates aren't required. If set, the notification will only be shown to players after the start date and until the end date. - +
diff --git a/projects/gameboard-ui/src/app/utility/services/toast.service.ts b/projects/gameboard-ui/src/app/utility/services/toast.service.ts index bef78edf..8afa4d7a 100644 --- a/projects/gameboard-ui/src/app/utility/services/toast.service.ts +++ b/projects/gameboard-ui/src/app/utility/services/toast.service.ts @@ -1,10 +1,12 @@ import { Injectable } from '@angular/core'; import Toastify from "toastify-js"; -import { ConfigService } from '../config.service'; import { IconDefinition } from '@fortawesome/fontawesome-svg-core'; import { FontAwesomeService } from '../../services/font-awesome.service'; +import { MarkdownService, ParseOptions } from 'ngx-markdown'; export interface ToastOptions { + allowMarkdown?: boolean; + avatarUrl?: string; text: string; duration?: number; destination?: string; @@ -23,14 +25,14 @@ interface FixedToastOptions { gravity: "top" | "bottom"; position: "center" | "right" | "left"; showCloseIcon: boolean; - style: { [rule: string]: string }; + style?: { [rule: string]: string }; } @Injectable({ providedIn: 'root' }) export class ToastService { constructor( - private config: ConfigService, - private faService: FontAwesomeService) { } + private faService: FontAwesomeService, + private markdownService: MarkdownService) { } private FIXED_OPTIONS: FixedToastOptions = { className: "gb-toast", @@ -38,11 +40,7 @@ export class ToastService { escapeMarkup: false, gravity: "top", position: "center", - showCloseIcon: true, - style: { - background: "var(--dark)", - border: `solid 1px ${this.config.settings.custom_background || "var(--light)"}` - }, + showCloseIcon: true }; showMessage(text: string) { @@ -56,8 +54,13 @@ export class ToastService { // to decouple direct reliance on toastify and enforce uniform // behavior across gameboard private toVendorOpts(options: ToastOptions): Toastify.Options { + // toastify requires "avatar", not "avatarUrl" + (options as any).avatar = options.avatarUrl; + delete options.avatarUrl; + const text = options.allowMarkdown === false ? options.text : this.markdownService.parse(options.text); + const iconMarkup = options.faIcon ? this.faService.iconToSvg(options.faIcon) : null; - const textTemplate = `
${iconMarkup || ""}${options.text}
`; + const textTemplate = `
${iconMarkup || ""}${text}
`; return { ...options, diff --git a/projects/gameboard-ui/src/scss/_toastify.scss b/projects/gameboard-ui/src/scss/_toastify.scss index a9480aba..ca789f70 100644 --- a/projects/gameboard-ui/src/scss/_toastify.scss +++ b/projects/gameboard-ui/src/scss/_toastify.scss @@ -1,21 +1,35 @@ +@import "../scss/variables"; + .gb-toast { - color: var(--light); + background-color: $dark !important; + background-image: none !important; + border: solid 2px $info; + border-radius: 6px; + padding-left: 1rem; padding-right: 1rem; + p { + margin: 0; + } + .toast-container { - display: flex; + display: inline-flex; align-items: center; justify-content: center; + + strong { + color: $info; + } } svg { display: block; - fill: var(--light) !important; + fill: $gray-100 !important; margin-right: 0.5rem; } button.toast-close { - color: var(--light) !important; + color: $gray-100 !important; font-size: 0.8rem; padding: 0; margin-left: 0.75rem; diff --git a/projects/gameboard-ui/src/styles.scss b/projects/gameboard-ui/src/styles.scss index 49ee8477..56914efa 100644 --- a/projects/gameboard-ui/src/styles.scss +++ b/projects/gameboard-ui/src/styles.scss @@ -151,7 +151,6 @@ h5 { } /* TABLE-Y STUFF */ - th[align="right"] { text-align: right; } @@ -160,6 +159,21 @@ th[align="center"] { text-align: center; } +.gameboard-table { + width: 100%; + + thead { + border-bottom: solid 1px $foreground; + font-size: 1.2rem; + text-transform: uppercase; + width: 100%; + + th { + padding-bottom: 0.5rem; + } + } +} + .spacer { flex: 1; } @@ -215,7 +229,7 @@ a[routerLink] { font-size: 1.2rem; } -app-spinner svg { +app-spinner svg.default-theme { rect, path { fill: theme-color("success"); @@ -307,6 +321,10 @@ th[align="left"] { color: $foreground; } +.cursor-help { + cursor: help; +} + .cursor-pointer { cursor: pointer; } @@ -332,21 +350,6 @@ th[align="left"] { font-weight: bold !important; } -.gameboard-table { - width: 100%; - - thead { - border-bottom: solid 1px $foreground; - font-size: 1.2rem; - text-transform: uppercase; - width: 100%; - - th { - padding-bottom: 0.5rem; - } - } -} - .text-capitalize { text-transform: capitalize; }