From eacdb6b8a802a5f03d09797db6e516e811e307ca Mon Sep 17 00:00:00 2001 From: Addison Beck Date: Thu, 4 Jan 2024 19:36:19 -0600 Subject: [PATCH] [AC-1743] pt. 2: Update eslintrc and fix any errors (#393) * Sync eslintrc with clients repo * Autofix one eslint error * Add type attributes to buttons for eslint * Properly destroy ApiKeyComponent * Fix eslint issues related to state * Fix eslint warnings for default named imports * Ran prettier * Be more proactive about an unsubscribe * Rework subscription --- .eslintrc.json | 127 ++-- jslib/common/spec/domain/attachment.spec.ts | 2 +- jslib/common/spec/domain/cipher.spec.ts | 2 +- jslib/common/spec/domain/encString.spec.ts | 2 +- jslib/common/spec/domain/login.spec.ts | 2 +- jslib/common/spec/domain/send.spec.ts | 2 +- jslib/common/spec/domain/sendAccess.spec.ts | 2 +- ...bitwardenPasswordProtectedImporter.spec.ts | 2 +- jslib/common/spec/matchers/to-equal-buffer.ts | 2 +- .../spec/services/import.service.spec.ts | 2 +- jslib/common/spec/utils.ts | 2 +- .../webCryptoFunction.service.spec.ts | 2 +- .../common/src/abstractions/state.service.ts | 6 +- .../src/services/environment.service.ts | 14 +- jslib/common/src/services/state.service.ts | 17 +- .../src/services/vaultTimeout.service.ts | 5 +- .../node/spec/cli/consoleLog.service.spec.ts | 1 - package-lock.json | 614 ++++++++++++++++++ package.json | 4 + src/app/accounts/apiKey.component.ts | 13 +- src/app/tabs/dashboard.component.html | 18 +- src/services/state.service.ts | 4 +- tsconfig.eslint.json | 28 + 23 files changed, 794 insertions(+), 79 deletions(-) create mode 100644 tsconfig.eslint.json diff --git a/.eslintrc.json b/.eslintrc.json index a28277d0..07e0dac9 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -4,53 +4,92 @@ "browser": true, "node": true }, - "parser": "@typescript-eslint/parser", - "plugins": ["@typescript-eslint"], - "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/recommended", - "plugin:import/recommended", - "plugin:import/typescript", - "prettier" - ], - "rules": { - "@typescript-eslint/no-explicit-any": "off", // TODO: This should be re-enabled - "@typescript-eslint/no-unused-vars": ["warn", { "args": "none" }], - "@typescript-eslint/explicit-member-accessibility": [ - "error", - { - "accessibility": "no-public" - } - ], - "@typescript-eslint/no-this-alias": [ - "error", - { - "allowedNames": ["self"] - } - ], - "no-console": "warn", - "import/no-unresolved": "off", // TODO: Look into turning off once each package is an actual package. - "import/order": [ - "error", - { - "alphabetize": { - "order": "asc" + "overrides": [ + { + "files": ["*.ts", "*.js"], + "plugins": ["@typescript-eslint", "rxjs", "rxjs-angular", "import"], + "parser": "@typescript-eslint/parser", + "parserOptions": { + "project": ["./tsconfig.eslint.json"], + "sourceType": "module", + "ecmaVersion": 2020 + }, + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:import/recommended", + "plugin:import/typescript", + "prettier", + "plugin:rxjs/recommended" + ], + "settings": { + "import/parsers": { + "@typescript-eslint/parser": [".ts"] }, - "newlines-between": "always", - "pathGroups": [ + "import/resolver": { + "typescript": { + "alwaysTryTypes": true + } + } + }, + "rules": { + "@typescript-eslint/explicit-member-accessibility": [ + "error", + { "accessibility": "no-public" } + ], + "@typescript-eslint/no-explicit-any": "off", // TODO: This should be re-enabled + "@typescript-eslint/no-misused-promises": ["error", { "checksVoidReturn": false }], + "@typescript-eslint/no-this-alias": ["error", { "allowedNames": ["self"] }], + "@typescript-eslint/no-unused-vars": ["error", { "args": "none" }], + "no-console": "error", + "import/no-unresolved": "off", // TODO: Look into turning off once each package is an actual package. + "import/order": [ + "error", { - "pattern": "jslib-*/**", - "group": "external", - "position": "after" - }, - { - "pattern": "src/**/*", - "group": "parent", - "position": "before" + "alphabetize": { + "order": "asc" + }, + "newlines-between": "always", + "pathGroups": [ + { + "pattern": "@/jslib/**/*", + "group": "external", + "position": "after" + }, + { + "pattern": "@/src/**/*", + "group": "parent", + "position": "before" + } + ], + "pathGroupsExcludedImportTypes": ["builtin"] } ], - "pathGroupsExcludedImportTypes": ["builtin"] + "rxjs-angular/prefer-takeuntil": "error", + "rxjs/no-exposed-subjects": ["error", { "allowProtected": true }], + "no-restricted-syntax": [ + "error", + { + "message": "Calling `svgIcon` directly is not allowed", + "selector": "CallExpression[callee.name='svgIcon']" + }, + { + "message": "Accessing FormGroup using `get` is not allowed, use `.value` instead", + "selector": "ChainExpression[expression.object.callee.property.name='get'][expression.property.name='value']" + } + ], + "curly": ["error", "all"], + "import/namespace": ["off"], // This doesn't resolve namespace imports correctly, but TS will throw for this anyway + "no-restricted-imports": ["error", { "patterns": ["src/**/*"] }] } - ] - } + }, + { + "files": ["*.html"], + "parser": "@angular-eslint/template-parser", + "plugins": ["@angular-eslint/template"], + "rules": { + "@angular-eslint/template/button-has-type": "error" + } + } + ] } diff --git a/jslib/common/spec/domain/attachment.spec.ts b/jslib/common/spec/domain/attachment.spec.ts index 318a2a80..9c8bd29a 100644 --- a/jslib/common/spec/domain/attachment.spec.ts +++ b/jslib/common/spec/domain/attachment.spec.ts @@ -1,4 +1,4 @@ -import Substitute, { Arg } from "@fluffy-spoon/substitute"; +import { Substitute, Arg } from "@fluffy-spoon/substitute"; import { CryptoService } from "@/jslib/common/src/abstractions/crypto.service"; import { AttachmentData } from "@/jslib/common/src/models/data/attachmentData"; diff --git a/jslib/common/spec/domain/cipher.spec.ts b/jslib/common/spec/domain/cipher.spec.ts index 936cc08a..b0e79930 100644 --- a/jslib/common/spec/domain/cipher.spec.ts +++ b/jslib/common/spec/domain/cipher.spec.ts @@ -1,4 +1,4 @@ -import Substitute, { Arg } from "@fluffy-spoon/substitute"; +import { Substitute, Arg } from "@fluffy-spoon/substitute"; import { CipherRepromptType } from "@/jslib/common/src/enums/cipherRepromptType"; import { CipherType } from "@/jslib/common/src/enums/cipherType"; diff --git a/jslib/common/spec/domain/encString.spec.ts b/jslib/common/spec/domain/encString.spec.ts index 8caaaf87..3932980c 100644 --- a/jslib/common/spec/domain/encString.spec.ts +++ b/jslib/common/spec/domain/encString.spec.ts @@ -1,4 +1,4 @@ -import Substitute, { Arg } from "@fluffy-spoon/substitute"; +import { Substitute, Arg } from "@fluffy-spoon/substitute"; import { CryptoService } from "@/jslib/common/src/abstractions/crypto.service"; import { EncryptionType } from "@/jslib/common/src/enums/encryptionType"; diff --git a/jslib/common/spec/domain/login.spec.ts b/jslib/common/spec/domain/login.spec.ts index 56f8c2c3..51ad07f5 100644 --- a/jslib/common/spec/domain/login.spec.ts +++ b/jslib/common/spec/domain/login.spec.ts @@ -1,4 +1,4 @@ -import Substitute, { Arg } from "@fluffy-spoon/substitute"; +import { Substitute, Arg } from "@fluffy-spoon/substitute"; import { UriMatchType } from "@/jslib/common/src/enums/uriMatchType"; import { LoginData } from "@/jslib/common/src/models/data/loginData"; diff --git a/jslib/common/spec/domain/send.spec.ts b/jslib/common/spec/domain/send.spec.ts index c25bab3a..ab34895f 100644 --- a/jslib/common/spec/domain/send.spec.ts +++ b/jslib/common/spec/domain/send.spec.ts @@ -1,4 +1,4 @@ -import Substitute, { Arg, SubstituteOf } from "@fluffy-spoon/substitute"; +import { Substitute, Arg, SubstituteOf } from "@fluffy-spoon/substitute"; import { CryptoService } from "@/jslib/common/src/abstractions/crypto.service"; import { SendType } from "@/jslib/common/src/enums/sendType"; diff --git a/jslib/common/spec/domain/sendAccess.spec.ts b/jslib/common/spec/domain/sendAccess.spec.ts index f10dfe4d..95373f06 100644 --- a/jslib/common/spec/domain/sendAccess.spec.ts +++ b/jslib/common/spec/domain/sendAccess.spec.ts @@ -1,4 +1,4 @@ -import Substitute, { Arg } from "@fluffy-spoon/substitute"; +import { Substitute, Arg } from "@fluffy-spoon/substitute"; import { SendType } from "@/jslib/common/src/enums/sendType"; import { SendAccess } from "@/jslib/common/src/models/domain/sendAccess"; diff --git a/jslib/common/spec/importers/bitwardenPasswordProtectedImporter.spec.ts b/jslib/common/spec/importers/bitwardenPasswordProtectedImporter.spec.ts index e44ce987..81b0e233 100644 --- a/jslib/common/spec/importers/bitwardenPasswordProtectedImporter.spec.ts +++ b/jslib/common/spec/importers/bitwardenPasswordProtectedImporter.spec.ts @@ -1,4 +1,4 @@ -import Substitute, { Arg, SubstituteOf } from "@fluffy-spoon/substitute"; +import { Substitute, Arg, SubstituteOf } from "@fluffy-spoon/substitute"; import { CryptoService } from "@/jslib/common/src/abstractions/crypto.service"; import { I18nService } from "@/jslib/common/src/abstractions/i18n.service"; diff --git a/jslib/common/spec/matchers/to-equal-buffer.ts b/jslib/common/spec/matchers/to-equal-buffer.ts index 5639f10a..e723d85a 100644 --- a/jslib/common/spec/matchers/to-equal-buffer.ts +++ b/jslib/common/spec/matchers/to-equal-buffer.ts @@ -6,7 +6,7 @@ */ export const toEqualBuffer: jest.CustomMatcher = function ( received: ArrayBuffer | Uint8Array, - expected: ArrayBuffer | Uint8Array, + expected: ArrayBuffer | Uint8Array ) { received = new Uint8Array(received); expected = new Uint8Array(expected); diff --git a/jslib/common/spec/services/import.service.spec.ts b/jslib/common/spec/services/import.service.spec.ts index 9625667a..6feaf2c4 100644 --- a/jslib/common/spec/services/import.service.spec.ts +++ b/jslib/common/spec/services/import.service.spec.ts @@ -1,4 +1,4 @@ -import Substitute, { SubstituteOf } from "@fluffy-spoon/substitute"; +import { Substitute, SubstituteOf } from "@fluffy-spoon/substitute"; import { ApiService } from "@/jslib/common/src/abstractions/api.service"; import { CipherService } from "@/jslib/common/src/abstractions/cipher.service"; diff --git a/jslib/common/spec/utils.ts b/jslib/common/spec/utils.ts index 8a1e4a18..e16bcb49 100644 --- a/jslib/common/spec/utils.ts +++ b/jslib/common/spec/utils.ts @@ -1,4 +1,4 @@ -import Substitute, { Arg } from "@fluffy-spoon/substitute"; +import { Substitute, Arg } from "@fluffy-spoon/substitute"; import { EncString } from "@/jslib/common/src/models/domain/encString"; diff --git a/jslib/common/spec/web/services/webCryptoFunction.service.spec.ts b/jslib/common/spec/web/services/webCryptoFunction.service.spec.ts index f4940397..06a188df 100644 --- a/jslib/common/spec/web/services/webCryptoFunction.service.spec.ts +++ b/jslib/common/spec/web/services/webCryptoFunction.service.spec.ts @@ -1,4 +1,4 @@ -import Substitute from "@fluffy-spoon/substitute"; +import { Substitute } from "@fluffy-spoon/substitute"; import { PlatformUtilsService } from "@/jslib/common/src/abstractions/platformUtils.service"; import { Utils } from "@/jslib/common/src/misc/utils"; diff --git a/jslib/common/src/abstractions/state.service.ts b/jslib/common/src/abstractions/state.service.ts index ebf94df3..e74470bd 100644 --- a/jslib/common/src/abstractions/state.service.ts +++ b/jslib/common/src/abstractions/state.service.ts @@ -1,4 +1,4 @@ -import { BehaviorSubject } from "rxjs"; +import { Observable } from "rxjs"; import { KdfType } from "../enums/kdfType"; import { ThemeType } from "../enums/themeType"; @@ -25,8 +25,8 @@ import { FolderView } from "../models/view/folderView"; import { SendView } from "../models/view/sendView"; export abstract class StateService { - accounts: BehaviorSubject<{ [userId: string]: T }>; - activeAccount: BehaviorSubject; + accounts$: Observable<{ [userId: string]: T }>; + activeAccount$: Observable; addAccount: (account: T) => Promise; setActiveUser: (userId: string) => Promise; diff --git a/jslib/common/src/services/environment.service.ts b/jslib/common/src/services/environment.service.ts index b13afd59..16683201 100644 --- a/jslib/common/src/services/environment.service.ts +++ b/jslib/common/src/services/environment.service.ts @@ -1,4 +1,4 @@ -import { Observable, Subject } from "rxjs"; +import { concatMap, distinctUntilChanged, Observable, Subject } from "rxjs"; import { EnvironmentService as EnvironmentServiceAbstraction, @@ -21,9 +21,15 @@ export class EnvironmentService implements EnvironmentServiceAbstraction { private keyConnectorUrl: string; constructor(private stateService: StateService) { - this.stateService.activeAccount.subscribe(async () => { - await this.setUrlsFromStorage(); - }); + this.stateService.activeAccount$ + .pipe( + // Use == here to not trigger on undefined -> null transition + distinctUntilChanged((oldUserId: string, newUserId: string) => oldUserId == newUserId), + concatMap(async () => { + await this.setUrlsFromStorage(); + }) + ) + .subscribe(); } hasBaseUrl() { diff --git a/jslib/common/src/services/state.service.ts b/jslib/common/src/services/state.service.ts index 39a08f50..78ddaf82 100644 --- a/jslib/common/src/services/state.service.ts +++ b/jslib/common/src/services/state.service.ts @@ -52,8 +52,11 @@ export class StateService< TAccount extends Account = Account > implements StateServiceAbstraction { - accounts = new BehaviorSubject<{ [userId: string]: TAccount }>({}); - activeAccount = new BehaviorSubject(null); + protected accountsSubject = new BehaviorSubject<{ [userId: string]: TAccount }>({}); + accounts$ = this.accountsSubject.asObservable(); + + protected activeAccountSubject = new BehaviorSubject(null); + activeAccount$ = this.activeAccountSubject.asObservable(); protected state: State = new State( this.createGlobals() @@ -100,7 +103,7 @@ export class StateService< this.state.activeUserId = storedActiveUser; } await this.pushAccounts(); - this.activeAccount.next(this.state.activeUserId); + this.activeAccountSubject.next(this.state.activeUserId); } async syncAccountFromDisk(userId: string) { @@ -120,14 +123,14 @@ export class StateService< await this.scaffoldNewAccountStorage(account); await this.setLastActive(new Date().getTime(), { userId: account.profile.userId }); await this.setActiveUser(account.profile.userId); - this.activeAccount.next(account.profile.userId); + this.activeAccountSubject.next(account.profile.userId); } async setActiveUser(userId: string): Promise { this.clearDecryptedDataForActiveUser(); this.state.activeUserId = userId; await this.storageService.save(keys.activeUserId, userId); - this.activeAccount.next(this.state.activeUserId); + this.activeAccountSubject.next(this.state.activeUserId); await this.pushAccounts(); } @@ -2338,11 +2341,11 @@ export class StateService< protected async pushAccounts(): Promise { await this.pruneInMemoryAccounts(); if (this.state?.accounts == null || Object.keys(this.state.accounts).length < 1) { - this.accounts.next(null); + this.accountsSubject.next(null); return; } - this.accounts.next(this.state.accounts); + this.accountsSubject.next(this.state.accounts); } protected reconcileOptions( diff --git a/jslib/common/src/services/vaultTimeout.service.ts b/jslib/common/src/services/vaultTimeout.service.ts index a6513d65..e5cd1d81 100644 --- a/jslib/common/src/services/vaultTimeout.service.ts +++ b/jslib/common/src/services/vaultTimeout.service.ts @@ -1,3 +1,5 @@ +import { firstValueFrom } from "rxjs"; + import { CipherService } from "../abstractions/cipher.service"; import { CollectionService } from "../abstractions/collection.service"; import { CryptoService } from "../abstractions/crypto.service"; @@ -67,7 +69,8 @@ export class VaultTimeoutService implements VaultTimeoutServiceAbstraction { return; } - for (const userId in this.stateService.accounts.getValue()) { + const accounts = await firstValueFrom(this.stateService.accounts$); + for (const userId in accounts) { if (userId != null && (await this.shouldLock(userId))) { await this.executeTimeoutAction(userId); } diff --git a/jslib/node/spec/cli/consoleLog.service.spec.ts b/jslib/node/spec/cli/consoleLog.service.spec.ts index d5970cdf..eaf54953 100644 --- a/jslib/node/spec/cli/consoleLog.service.spec.ts +++ b/jslib/node/spec/cli/consoleLog.service.spec.ts @@ -2,7 +2,6 @@ import { interceptConsole, restoreConsole, } from "@/jslib/common/spec/services/consoleLog.service.spec"; - import { ConsoleLogService } from "@/jslib/node/src/cli/services/consoleLog.service"; let caughtMessage: any = {}; diff --git a/package-lock.json b/package-lock.json index fbef6f56..98de70cf 100644 --- a/package-lock.json +++ b/package-lock.json @@ -50,6 +50,8 @@ }, "devDependencies": { "@angular-devkit/build-angular": "15.2.9", + "@angular-eslint/eslint-plugin-template": "^17.1.1", + "@angular-eslint/template-parser": "^17.1.1", "@angular/compiler-cli": "15.2.9", "@angular/platform-browser-dynamic": "15.2.9", "@fluffy-spoon/substitute": "1.208.0", @@ -87,6 +89,8 @@ "eslint-config-prettier": "8.10.0", "eslint-import-resolver-typescript": "2.7.1", "eslint-plugin-import": "2.28.1", + "eslint-plugin-rxjs": "^5.0.3", + "eslint-plugin-rxjs-angular": "^2.0.1", "form-data": "4.0.0", "html-loader": "3.1.2", "html-webpack-plugin": "5.5.3", @@ -864,6 +868,374 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, + "node_modules/@angular-eslint/bundled-angular-compiler": { + "version": "17.1.1", + "resolved": "https://registry.npmjs.org/@angular-eslint/bundled-angular-compiler/-/bundled-angular-compiler-17.1.1.tgz", + "integrity": "sha512-xRlSh9qjdUdUKAy/0UQsxX7wf1tHApAsHsfismebPriqfmVAPyEg4HBrM8ImWaZxiqaTGC1AyHsUBQD5FK8o6w==", + "dev": true + }, + "node_modules/@angular-eslint/eslint-plugin-template": { + "version": "17.1.1", + "resolved": "https://registry.npmjs.org/@angular-eslint/eslint-plugin-template/-/eslint-plugin-template-17.1.1.tgz", + "integrity": "sha512-unZ6QNwtxuB8Eni7UPdw7uK6iZipZUXIsH+ZuLMOxwFgGMqeRnpv8SW0212rto3d/Ec0jESzVHKcwZ9pT+jxgw==", + "dev": true, + "dependencies": { + "@angular-eslint/bundled-angular-compiler": "17.1.1", + "@angular-eslint/utils": "17.1.1", + "@typescript-eslint/type-utils": "6.13.1", + "@typescript-eslint/utils": "6.13.1", + "aria-query": "5.3.0", + "axobject-query": "4.0.0" + }, + "peerDependencies": { + "eslint": "^7.20.0 || ^8.0.0", + "typescript": "*" + } + }, + "node_modules/@angular-eslint/eslint-plugin-template/node_modules/@typescript-eslint/scope-manager": { + "version": "6.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.13.1.tgz", + "integrity": "sha512-BW0kJ7ceiKi56GbT2KKzZzN+nDxzQK2DS6x0PiSMPjciPgd/JRQGMibyaN2cPt2cAvuoH0oNvn2fwonHI+4QUQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.13.1", + "@typescript-eslint/visitor-keys": "6.13.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@angular-eslint/eslint-plugin-template/node_modules/@typescript-eslint/type-utils": { + "version": "6.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.13.1.tgz", + "integrity": "sha512-A2qPlgpxx2v//3meMqQyB1qqTg1h1dJvzca7TugM3Yc2USDY+fsRBiojAEo92HO7f5hW5mjAUF6qobOPzlBCBQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "6.13.1", + "@typescript-eslint/utils": "6.13.1", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@angular-eslint/eslint-plugin-template/node_modules/@typescript-eslint/types": { + "version": "6.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.13.1.tgz", + "integrity": "sha512-gjeEskSmiEKKFIbnhDXUyiqVma1gRCQNbVZ1C8q7Zjcxh3WZMbzWVfGE9rHfWd1msQtPS0BVD9Jz9jded44eKg==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@angular-eslint/eslint-plugin-template/node_modules/@typescript-eslint/typescript-estree": { + "version": "6.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.13.1.tgz", + "integrity": "sha512-sBLQsvOC0Q7LGcUHO5qpG1HxRgePbT6wwqOiGLpR8uOJvPJbfs0mW3jPA3ujsDvfiVwVlWUDESNXv44KtINkUQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.13.1", + "@typescript-eslint/visitor-keys": "6.13.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@angular-eslint/eslint-plugin-template/node_modules/@typescript-eslint/utils": { + "version": "6.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.13.1.tgz", + "integrity": "sha512-ouPn/zVoan92JgAegesTXDB/oUp6BP1v8WpfYcqh649ejNc9Qv+B4FF2Ff626kO1xg0wWwwG48lAJ4JuesgdOw==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.13.1", + "@typescript-eslint/types": "6.13.1", + "@typescript-eslint/typescript-estree": "6.13.1", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/@angular-eslint/eslint-plugin-template/node_modules/@typescript-eslint/visitor-keys": { + "version": "6.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.13.1.tgz", + "integrity": "sha512-NDhQUy2tg6XGNBGDRm1XybOHSia8mcXmlbKWoQP+nm1BIIMxa55shyJfZkHpEBN62KNPLrocSM2PdPcaLgDKMQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.13.1", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@angular-eslint/eslint-plugin-template/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@angular-eslint/eslint-plugin-template/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@angular-eslint/eslint-plugin-template/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/@angular-eslint/template-parser": { + "version": "17.1.1", + "resolved": "https://registry.npmjs.org/@angular-eslint/template-parser/-/template-parser-17.1.1.tgz", + "integrity": "sha512-ofL46rNhRVeSxrSQF0vwhKMco+vJuo+ZGjSOzFmT9N3KAMB0j+WXTbpyGGMy0gQSBc4W6p+j+zxGa2CR2xb6wA==", + "dev": true, + "dependencies": { + "@angular-eslint/bundled-angular-compiler": "17.1.1", + "eslint-scope": "^7.0.0" + }, + "peerDependencies": { + "eslint": "^7.20.0 || ^8.0.0", + "typescript": "*" + } + }, + "node_modules/@angular-eslint/template-parser/node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@angular-eslint/template-parser/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/@angular-eslint/utils": { + "version": "17.1.1", + "resolved": "https://registry.npmjs.org/@angular-eslint/utils/-/utils-17.1.1.tgz", + "integrity": "sha512-CTNPOb05S/DII/Fm8JYUvKo+B4u/ctHjGJ0X1YXUR0q31oaGqTE3KePGq76+Y6swRDf9NjUIcfcnZp3u3j4CBQ==", + "dev": true, + "dependencies": { + "@angular-eslint/bundled-angular-compiler": "17.1.1", + "@typescript-eslint/utils": "6.13.1" + }, + "peerDependencies": { + "eslint": "^7.20.0 || ^8.0.0", + "typescript": "*" + } + }, + "node_modules/@angular-eslint/utils/node_modules/@typescript-eslint/scope-manager": { + "version": "6.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.13.1.tgz", + "integrity": "sha512-BW0kJ7ceiKi56GbT2KKzZzN+nDxzQK2DS6x0PiSMPjciPgd/JRQGMibyaN2cPt2cAvuoH0oNvn2fwonHI+4QUQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.13.1", + "@typescript-eslint/visitor-keys": "6.13.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@angular-eslint/utils/node_modules/@typescript-eslint/types": { + "version": "6.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.13.1.tgz", + "integrity": "sha512-gjeEskSmiEKKFIbnhDXUyiqVma1gRCQNbVZ1C8q7Zjcxh3WZMbzWVfGE9rHfWd1msQtPS0BVD9Jz9jded44eKg==", + "dev": true, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@angular-eslint/utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "6.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.13.1.tgz", + "integrity": "sha512-sBLQsvOC0Q7LGcUHO5qpG1HxRgePbT6wwqOiGLpR8uOJvPJbfs0mW3jPA3ujsDvfiVwVlWUDESNXv44KtINkUQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.13.1", + "@typescript-eslint/visitor-keys": "6.13.1", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@angular-eslint/utils/node_modules/@typescript-eslint/utils": { + "version": "6.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.13.1.tgz", + "integrity": "sha512-ouPn/zVoan92JgAegesTXDB/oUp6BP1v8WpfYcqh649ejNc9Qv+B4FF2Ff626kO1xg0wWwwG48lAJ4JuesgdOw==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.13.1", + "@typescript-eslint/types": "6.13.1", + "@typescript-eslint/typescript-estree": "6.13.1", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/@angular-eslint/utils/node_modules/@typescript-eslint/visitor-keys": { + "version": "6.13.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.13.1.tgz", + "integrity": "sha512-NDhQUy2tg6XGNBGDRm1XybOHSia8mcXmlbKWoQP+nm1BIIMxa55shyJfZkHpEBN62KNPLrocSM2PdPcaLgDKMQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "6.13.1", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@angular-eslint/utils/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@angular-eslint/utils/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@angular-eslint/utils/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, "node_modules/@angular/animations": { "version": "15.2.9", "resolved": "https://registry.npmjs.org/@angular/animations/-/animations-15.2.9.tgz", @@ -5961,6 +6333,25 @@ } } }, + "node_modules/@typescript-eslint/experimental-utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-5.62.0.tgz", + "integrity": "sha512-RTXpeB3eMkpoclG3ZHft6vG/Z30azNHuqY6wKPBHlVMZFuEvrtlEDe8gMqDb+SO+9hjC/pLekeSCryf9vMZlCw==", + "dev": true, + "dependencies": { + "@typescript-eslint/utils": "5.62.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, "node_modules/@typescript-eslint/parser": { "version": "5.62.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", @@ -6754,6 +7145,15 @@ "sprintf-js": "~1.0.2" } }, + "node_modules/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dev": true, + "dependencies": { + "dequal": "^2.0.3" + } + }, "node_modules/array-buffer-byte-length": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", @@ -7003,6 +7403,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/axobject-query": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.0.0.tgz", + "integrity": "sha512-+60uv1hiVFhHZeO+Lz0RYzsVHy5Wr1ayX0mwda9KPDVLNJgZ1T9Ny7VmFbLDzxsH0D87I86vgj3gFrjTJUYznw==", + "dev": true, + "dependencies": { + "dequal": "^2.0.3" + } + }, "node_modules/babel-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", @@ -7200,6 +7609,17 @@ "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", "dev": true }, + "node_modules/bent": { + "version": "7.3.12", + "resolved": "https://registry.npmjs.org/bent/-/bent-7.3.12.tgz", + "integrity": "sha512-T3yrKnVGB63zRuoco/7Ybl7BwwGZR0lceoVG5XmQyMIH9s19SV5m+a8qam4if0zQuAmOQTyPTPmsQBdAorGK3w==", + "dev": true, + "dependencies": { + "bytesish": "^0.4.1", + "caseless": "~0.12.0", + "is-stream": "^2.0.0" + } + }, "node_modules/big-integer": { "version": "1.6.51", "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", @@ -7627,6 +8047,12 @@ "node": ">= 0.8" } }, + "node_modules/bytesish": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/bytesish/-/bytesish-0.4.4.tgz", + "integrity": "sha512-i4uu6M4zuMUiyfZN4RU2+i9+peJh//pXhd9x1oSe1LBkZ3LEbCoygu8W0bXTukU1Jme2txKuotpCZRaC3FLxcQ==", + "dev": true + }, "node_modules/cacache": { "version": "17.0.4", "resolved": "https://registry.npmjs.org/cacache/-/cacache-17.0.4.tgz", @@ -7785,6 +8211,12 @@ } ] }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", + "dev": true + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -8121,6 +8553,15 @@ "node": ">= 10" } }, + "node_modules/common-tags": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.2.tgz", + "integrity": "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==", + "dev": true, + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/commondir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", @@ -8824,6 +9265,18 @@ } } }, + "node_modules/decamelize": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-5.0.1.tgz", + "integrity": "sha512-VfxadyCECXgQlkoEAjeghAr5gY3Hf+IKjKb+X8tGVDtveCjN+USwprd2q3QXBR9T1+x2DG0XZF5/w+7HAtSaXA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/decimal.js": { "version": "10.4.3", "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", @@ -9096,6 +9549,15 @@ "node": ">= 0.6.0" } }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/destroy": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", @@ -10469,6 +10931,21 @@ "eslint": ">=7.0.0" } }, + "node_modules/eslint-etc": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/eslint-etc/-/eslint-etc-5.2.1.tgz", + "integrity": "sha512-lFJBSiIURdqQKq9xJhvSJFyPA+VeTh5xvk24e8pxVL7bwLBtGF60C/KRkLTMrvCZ6DA3kbPuYhLWY0TZMlqTsg==", + "dev": true, + "dependencies": { + "@typescript-eslint/experimental-utils": "^5.0.0", + "tsutils": "^3.17.1", + "tsutils-etc": "^1.4.1" + }, + "peerDependencies": { + "eslint": "^8.0.0", + "typescript": ">=4.0.0" + } + }, "node_modules/eslint-import-resolver-node": { "version": "0.3.9", "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", @@ -10677,6 +11154,44 @@ "semver": "bin/semver.js" } }, + "node_modules/eslint-plugin-rxjs": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-rxjs/-/eslint-plugin-rxjs-5.0.3.tgz", + "integrity": "sha512-fcVkqLmYLRfRp+ShafjpUKuaZ+cw/sXAvM5dfSxiEr7M28QZ/NY7vaOr09FB4rSaZsQyLBnNPh5SL+4EgKjh8Q==", + "dev": true, + "dependencies": { + "@typescript-eslint/experimental-utils": "^5.0.0", + "common-tags": "^1.8.0", + "decamelize": "^5.0.0", + "eslint-etc": "^5.1.0", + "requireindex": "~1.2.0", + "rxjs-report-usage": "^1.0.4", + "tslib": "^2.0.0", + "tsutils": "^3.0.0", + "tsutils-etc": "^1.4.1" + }, + "peerDependencies": { + "eslint": "^8.0.0", + "typescript": ">=4.0.0" + } + }, + "node_modules/eslint-plugin-rxjs-angular": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-rxjs-angular/-/eslint-plugin-rxjs-angular-2.0.1.tgz", + "integrity": "sha512-HJ/JHhjDJKyFUmM8o7rS91WNkNv7W7Z/okR5X3hqG7tKVMLOJi4T63Aa74ECuCdowmdfW75p2RrW4R8WeoZIKQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/experimental-utils": "^5.0.0", + "common-tags": "^1.8.0", + "eslint-etc": "^5.0.0", + "requireindex": "~1.2.0", + "tslib": "^2.0.0" + }, + "peerDependencies": { + "eslint": "^8.0.0", + "typescript": ">=4.0.0" + } + }, "node_modules/eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", @@ -18620,6 +19135,15 @@ "node": ">=0.10.0" } }, + "node_modules/requireindex": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/requireindex/-/requireindex-1.2.0.tgz", + "integrity": "sha512-L9jEkOi3ASd9PYit2cwRfyppc9NoABujTP8/5gFcbERmo5jUoAKovIC3fsF17pkTnGsrByysqX+Kxd2OTNI1ww==", + "dev": true, + "engines": { + "node": ">=0.10.5" + } + }, "node_modules/requires-port": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", @@ -18884,6 +19408,66 @@ "tslib": "^2.1.0" } }, + "node_modules/rxjs-report-usage": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/rxjs-report-usage/-/rxjs-report-usage-1.0.6.tgz", + "integrity": "sha512-omv1DIv5z1kV+zDAEjaDjWSkx8w5TbFp5NZoPwUipwzYVcor/4So9ZU3bUyQ1c8lxY5Q0Es/ztWW7PGjY7to0Q==", + "dev": true, + "dependencies": { + "@babel/parser": "^7.10.3", + "@babel/traverse": "^7.10.3", + "@babel/types": "^7.10.3", + "bent": "~7.3.6", + "chalk": "~4.1.0", + "glob": "~7.2.0", + "prompts": "~2.4.2" + }, + "bin": { + "rxjs-report-usage": "bin/rxjs-report-usage" + } + }, + "node_modules/rxjs-report-usage/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/rxjs-report-usage/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rxjs-report-usage/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/safe-array-concat": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz", @@ -20619,6 +21203,18 @@ "utf8-byte-length": "^1.0.1" } }, + "node_modules/ts-api-utils": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", + "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", + "dev": true, + "engines": { + "node": ">=16.13.0" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, "node_modules/ts-jest": { "version": "29.1.1", "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.1.tgz", @@ -20814,6 +21410,24 @@ "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" } }, + "node_modules/tsutils-etc": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/tsutils-etc/-/tsutils-etc-1.4.2.tgz", + "integrity": "sha512-2Dn5SxTDOu6YWDNKcx1xu2YUy6PUeKrWZB/x2cQ8vY2+iz3JRembKn/iZ0JLT1ZudGNwQQvtFX9AwvRHbXuPUg==", + "dev": true, + "dependencies": { + "@types/yargs": "^17.0.0", + "yargs": "^17.0.0" + }, + "bin": { + "ts-flags": "bin/ts-flags", + "ts-kind": "bin/ts-kind" + }, + "peerDependencies": { + "tsutils": "^3.0.0", + "typescript": ">=4.0.0" + } + }, "node_modules/tsutils/node_modules/tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", diff --git a/package.json b/package.json index 7527ebd5..8aff657d 100644 --- a/package.json +++ b/package.json @@ -70,6 +70,8 @@ }, "devDependencies": { "@angular-devkit/build-angular": "15.2.9", + "@angular-eslint/eslint-plugin-template": "^17.1.1", + "@angular-eslint/template-parser": "^17.1.1", "@angular/compiler-cli": "15.2.9", "@angular/platform-browser-dynamic": "15.2.9", "@fluffy-spoon/substitute": "1.208.0", @@ -107,6 +109,8 @@ "eslint-config-prettier": "8.10.0", "eslint-import-resolver-typescript": "2.7.1", "eslint-plugin-import": "2.28.1", + "eslint-plugin-rxjs": "^5.0.3", + "eslint-plugin-rxjs-angular": "^2.0.1", "form-data": "4.0.0", "html-loader": "3.1.2", "html-webpack-plugin": "5.5.3", diff --git a/src/app/accounts/apiKey.component.ts b/src/app/accounts/apiKey.component.ts index 21ae191d..ff260e33 100644 --- a/src/app/accounts/apiKey.component.ts +++ b/src/app/accounts/apiKey.component.ts @@ -1,5 +1,6 @@ import { Component, Input, ViewChild, ViewContainerRef } from "@angular/core"; import { Router } from "@angular/router"; +import { takeUntil } from "rxjs"; import { ModalService } from "@/jslib/angular/src/services/modal.service"; import { AuthService } from "@/jslib/common/src/abstractions/auth.service"; @@ -17,6 +18,12 @@ import { EnvironmentComponent } from "./environment.component"; selector: "app-apiKey", templateUrl: "apiKey.component.html", }) +// There is an eslint exception made here due to semantics. +// The eslint rule expects a typical takeUntil() pattern involving component destruction. +// The only subscription in this component is closed from a child component, confusing eslint. +// https://github.com/cartant/eslint-plugin-rxjs-angular/blob/main/docs/rules/prefer-takeuntil.md +// +// eslint-disable-next-line rxjs-angular/prefer-takeuntil export class ApiKeyComponent { @ViewChild("environment", { read: ViewContainerRef, static: true }) environmentModal: ViewContainerRef; @@ -87,15 +94,17 @@ export class ApiKeyComponent { } async settings() { - const [modalRef, childComponent] = await this.modalService.openViewRef( + const [modalRef, childComponent] = await this.modalService.openViewRef( EnvironmentComponent, this.environmentModal ); - childComponent.onSaved.subscribe(() => { + // eslint-disable-next-line rxjs-angular/prefer-takeuntil + childComponent.onSaved.pipe(takeUntil(modalRef.onClosed)).subscribe(() => { modalRef.close(); }); } + toggleSecret() { this.showSecret = !this.showSecret; document.getElementById("client_secret").focus(); diff --git a/src/app/tabs/dashboard.component.html b/src/app/tabs/dashboard.component.html index a08a6176..bd33f23e 100644 --- a/src/app/tabs/dashboard.component.html +++ b/src/app/tabs/dashboard.component.html @@ -16,18 +16,23 @@ {{ "stopped" | i18n }}

-
-
- @@ -39,7 +44,12 @@

{{ "testingDesc" | i18n }}

-