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 }}
-